Coverage for little_loops / cli / adapt_agents_for_codex.py: 81%

42 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-06-29 00:55 -0500

1"""ll-adapt-agents-for-codex: thin alias for ``ll-adapt --host codex`` (agents). 

2 

3The implementation now lives in :mod:`little_loops.adapters.codex` and 

4:mod:`little_loops.adapters.core`. This module is kept for backward 

5compatibility; it re-exports the original helper names used by tests and 

6delegates processing to :class:`~little_loops.adapters.codex.CodexEmitter`. 

7""" 

8 

9from __future__ import annotations 

10 

11import argparse 

12import sys 

13from pathlib import Path 

14 

15from little_loops.adapters.codex import ( 

16 CodexEmitter, 

17 _MARKER, 

18 _extract_agent_short_desc as _extract_short_desc, 

19 _format_agent_toml, 

20) 

21from little_loops.adapters.core import _read_frontmatter, process_agents as _core_process_agents 

22from little_loops.session_store import DEFAULT_DB_PATH, cli_event_context 

23 

24__all__ = ["main_adapt_agents_for_codex"] 

25 

26_MAX_SHORT_DESC = 80 

27 

28 

29def _find_plugin_root() -> Path: 

30 from little_loops.skill_expander import _find_plugin_root as _fpr 

31 

32 return _fpr() 

33 

34 

35# --------------------------------------------------------------------------- 

36# Backward-compat function wrappers (original signatures) 

37# --------------------------------------------------------------------------- 

38 

39 

40def _extract_agent_frontmatter(text: str) -> dict | None: 

41 """Backward-compat wrapper around core._read_frontmatter.""" 

42 return _read_frontmatter(text) 

43 

44 

45def _emit_agent_toml(name: str, description: str, model: str, body: str) -> str: 

46 """Backward-compat wrapper (4-arg): delegates to _format_agent_toml with empty fm.""" 

47 return _format_agent_toml(name, description, model, body, {}) 

48 

49 

50def _process_agents( 

51 agents_dir: Path, 

52 codex_dir: Path, 

53 apply: bool, 

54 quiet: bool, 

55 only: str | None, 

56) -> tuple[int, int, int]: 

57 """Backward-compat wrapper: delegate to core traversal + CodexEmitter.""" 

58 return _core_process_agents(CodexEmitter(), agents_dir, codex_dir, apply, quiet, only) 

59 

60 

61# --------------------------------------------------------------------------- 

62# CLI entry point 

63# --------------------------------------------------------------------------- 

64 

65 

66def main_adapt_agents_for_codex() -> int: 

67 """Entry point for ll-adapt-agents-for-codex CLI.""" 

68 with cli_event_context(DEFAULT_DB_PATH, "ll-adapt-agents-for-codex", sys.argv[1:]): 

69 parser = argparse.ArgumentParser( 

70 prog="ll-adapt-agents-for-codex", 

71 description=( 

72 "Emit .codex/agents/<name>.toml files from agents/*.md for Codex subagent " 

73 "discovery. Dry-run by default; use --apply to write changes. " 

74 "(Thin alias for: ll-adapt --host codex)" 

75 ), 

76 formatter_class=argparse.RawDescriptionHelpFormatter, 

77 epilog=""" 

78Examples: 

79 ll-adapt-agents-for-codex # Dry-run: preview proposed changes 

80 ll-adapt-agents-for-codex --apply # Write .codex/agents/*.toml files 

81 ll-adapt-agents-for-codex --only codebase-analyzer --apply # Single agent 

82 ll-adapt-agents-for-codex --quiet # Suppress per-entry output 

83""", 

84 ) 

85 parser.add_argument( 

86 "--apply", 

87 action="store_true", 

88 default=False, 

89 help="Write .codex/agents/*.toml files (default: dry-run)", 

90 ) 

91 parser.add_argument( 

92 "--only", 

93 metavar="NAME", 

94 default=None, 

95 help="Restrict to a single agent by stem name (e.g. codebase-analyzer)", 

96 ) 

97 parser.add_argument( 

98 "--quiet", 

99 action="store_true", 

100 default=False, 

101 help="Suppress per-agent output; only print final summary", 

102 ) 

103 

104 args = parser.parse_args() 

105 

106 plugin_root = _find_plugin_root() 

107 agents_dir = plugin_root / "agents" 

108 codex_dir = plugin_root / ".codex" / "agents" 

109 

110 if not agents_dir.exists(): 

111 print(f"ERROR: agents directory not found: {agents_dir}", file=sys.stderr) 

112 return 1 

113 

114 mode = "APPLY" if args.apply else "DRY-RUN" 

115 if not args.quiet: 

116 print(f"ll-adapt-agents-for-codex [{mode}]") 

117 print(f"Agents dir: {agents_dir}") 

118 print(f"Output dir: {codex_dir}") 

119 if args.only: 

120 print(f"Filter: {args.only}") 

121 print() 

122 

123 adapted, skipped, errors = _process_agents( 

124 agents_dir, codex_dir, args.apply, args.quiet, args.only 

125 ) 

126 

127 print(f"\nDone: {adapted} adapted, {skipped} skipped, {errors} errors") 

128 return 0 if errors == 0 else 1