Coverage for little_loops / cli / adapt.py: 15%
53 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: unified host-parameterized skill/command/agent adapter.
3Dispatches to host-specific emitters via the :func:`resolve_emitter` registry.
4Dry-run by default; use ``--apply`` to write changes.
5"""
7from __future__ import annotations
9import argparse
10import sys
12from little_loops.adapters.core import AdapterError, process_agents, process_commands, process_skills, resolve_emitter
13from little_loops.session_store import DEFAULT_DB_PATH, cli_event_context
15__all__ = ["main_adapt"]
18def _find_plugin_root():
19 from little_loops.skill_expander import _find_plugin_root as _fpr
21 return _fpr()
24def main_adapt() -> int:
25 """Entry point for ``ll-adapt`` CLI."""
26 with cli_event_context(DEFAULT_DB_PATH, "ll-adapt", sys.argv[1:]):
27 parser = argparse.ArgumentParser(
28 prog="ll-adapt",
29 description=(
30 "Generate host-specific skill, command, and agent artefacts for a given host. "
31 "Dry-run by default; use --apply to write changes."
32 ),
33 formatter_class=argparse.RawDescriptionHelpFormatter,
34 epilog="""
35Examples:
36 ll-adapt --host codex # Dry-run: preview proposed changes
37 ll-adapt --host codex --apply # Write Codex artefacts
38 ll-adapt --host codex --only codebase-analyzer --apply # Single agent
39 ll-adapt --host codex --quiet # Suppress per-entry output
40""",
41 )
42 parser.add_argument(
43 "--host",
44 required=True,
45 metavar="HOST",
46 help="Target host (e.g. codex, omp)",
47 )
48 parser.add_argument(
49 "--apply",
50 action="store_true",
51 default=False,
52 help="Write changes (default: dry-run)",
53 )
54 parser.add_argument(
55 "--dry-run",
56 action="store_true",
57 default=False,
58 dest="dry_run",
59 help="Preview changes without writing (default behaviour; explicit alias)",
60 )
61 parser.add_argument(
62 "--only",
63 metavar="NAME",
64 default=None,
65 help="Restrict agent processing to a single agent by stem name",
66 )
67 parser.add_argument(
68 "--quiet",
69 action="store_true",
70 default=False,
71 help="Suppress per-entry output; only print final summary",
72 )
74 args = parser.parse_args()
75 apply = args.apply and not args.dry_run
77 try:
78 emitter = resolve_emitter(args.host)
79 except AdapterError as exc:
80 print(f"ERROR: {exc}", file=sys.stderr)
81 return 1
83 plugin_root = _find_plugin_root()
84 skills_dir = plugin_root / "skills"
85 commands_dir = plugin_root / "commands"
86 agents_dir = plugin_root / "agents"
88 mode = "APPLY" if apply else "DRY-RUN"
89 if not args.quiet:
90 print(f"ll-adapt --host {args.host} [{mode}]")
91 print(f"Plugin root: {plugin_root}")
92 if args.only:
93 print(f"Filter: {args.only}")
94 print()
96 total_adapted = total_skipped = total_errors = 0
98 # Skills
99 if skills_dir.exists():
100 s_adapted, s_skipped, s_errors = process_skills(
101 emitter, skills_dir, apply, args.quiet
102 )
103 total_adapted += s_adapted
104 total_skipped += s_skipped
105 total_errors += s_errors
107 # Commands
108 c_adapted, c_skipped, c_errors = process_commands(
109 emitter, commands_dir, skills_dir, apply, args.quiet
110 )
111 total_adapted += c_adapted
112 total_skipped += c_skipped
113 total_errors += c_errors
115 # Agents
116 if agents_dir.exists():
117 codex_dir = plugin_root / ".codex" / "agents"
118 a_adapted, a_skipped, a_errors = process_agents(
119 emitter, agents_dir, codex_dir, apply, args.quiet, args.only
120 )
121 total_adapted += a_adapted
122 total_skipped += a_skipped
123 total_errors += a_errors
125 print(f"\nDone: {total_adapted} adapted, {total_skipped} skipped, {total_errors} errors")
126 return 0 if total_errors == 0 else 1