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
« 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).
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"""
9from __future__ import annotations
11import argparse
12import sys
13from pathlib import Path
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
24__all__ = ["main_adapt_agents_for_codex"]
26_MAX_SHORT_DESC = 80
29def _find_plugin_root() -> Path:
30 from little_loops.skill_expander import _find_plugin_root as _fpr
32 return _fpr()
35# ---------------------------------------------------------------------------
36# Backward-compat function wrappers (original signatures)
37# ---------------------------------------------------------------------------
40def _extract_agent_frontmatter(text: str) -> dict | None:
41 """Backward-compat wrapper around core._read_frontmatter."""
42 return _read_frontmatter(text)
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, {})
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)
61# ---------------------------------------------------------------------------
62# CLI entry point
63# ---------------------------------------------------------------------------
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 )
104 args = parser.parse_args()
106 plugin_root = _find_plugin_root()
107 agents_dir = plugin_root / "agents"
108 codex_dir = plugin_root / ".codex" / "agents"
110 if not agents_dir.exists():
111 print(f"ERROR: agents directory not found: {agents_dir}", file=sys.stderr)
112 return 1
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()
123 adapted, skipped, errors = _process_agents(
124 agents_dir, codex_dir, args.apply, args.quiet, args.only
125 )
127 print(f"\nDone: {adapted} adapted, {skipped} skipped, {errors} errors")
128 return 0 if errors == 0 else 1