Coverage for little_loops / cli / adapt_skills_for_codex.py: 85%

41 statements  

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

1"""ll-adapt-skills-for-codex: thin alias for ``ll-adapt --host codex`` (skills + commands). 

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 _extract_skill_short_desc as _extract_short_desc, 

18 _insert_skill_fields as _insert_fields, 

19 _make_openai_yaml_content as _make_openai_yaml, 

20 _synthesized_skill_md, 

21 _title_case, 

22) 

23from little_loops.adapters.core import process_commands as _core_process_commands 

24from little_loops.adapters.core import process_skills as _core_process_skills 

25from little_loops.session_store import DEFAULT_DB_PATH, cli_event_context 

26 

27__all__ = ["main_adapt_skills_for_codex"] 

28 

29 

30def _find_plugin_root() -> Path: 

31 from little_loops.skill_expander import _find_plugin_root as _fpr 

32 

33 return _fpr() 

34 

35 

36# --------------------------------------------------------------------------- 

37# Backward-compat process wrappers (original 3/4-arg signatures) 

38# --------------------------------------------------------------------------- 

39 

40 

41def _process_skills( 

42 skills_dir: Path, apply: bool, quiet: bool 

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

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

45 return _core_process_skills(CodexEmitter(), skills_dir, apply, quiet) 

46 

47 

48def _process_commands( 

49 commands_dir: Path, skills_dir: Path, apply: bool, quiet: bool 

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

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

52 return _core_process_commands(CodexEmitter(), commands_dir, skills_dir, apply, quiet) 

53 

54 

55# --------------------------------------------------------------------------- 

56# CLI entry point 

57# --------------------------------------------------------------------------- 

58 

59 

60def main_adapt_skills_for_codex() -> int: 

61 """Entry point for ll-adapt-skills-for-codex CLI.""" 

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

63 parser = argparse.ArgumentParser( 

64 prog="ll-adapt-skills-for-codex", 

65 description=( 

66 "Add Codex Skills API frontmatter to ll skill SKILL.md files and " 

67 "bridge commands/*.md into skills/ll-<name>/ entries for Codex CLI. " 

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

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

70 ), 

71 formatter_class=argparse.RawDescriptionHelpFormatter, 

72 epilog=""" 

73Examples: 

74 ll-adapt-skills-for-codex # Dry-run: preview proposed changes 

75 ll-adapt-skills-for-codex --apply # Write skill frontmatter + bridge commands 

76 ll-adapt-skills-for-codex --quiet # Suppress per-entry output 

77""", 

78 ) 

79 parser.add_argument( 

80 "--apply", 

81 action="store_true", 

82 default=False, 

83 help="Write changes to SKILL.md files and create agents/openai.yaml (default: dry-run)", 

84 ) 

85 parser.add_argument( 

86 "--quiet", 

87 action="store_true", 

88 default=False, 

89 help="Suppress per-skill output; only print final summary", 

90 ) 

91 

92 args = parser.parse_args() 

93 

94 plugin_root = _find_plugin_root() 

95 skills_dir = plugin_root / "skills" 

96 commands_dir = plugin_root / "commands" 

97 

98 if not skills_dir.exists(): 

99 print(f"ERROR: skills directory not found: {skills_dir}", file=sys.stderr) 

100 return 1 

101 

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

103 if not args.quiet: 

104 print(f"ll-adapt-skills-for-codex [{mode}]") 

105 print(f"Skills dir: {skills_dir}") 

106 print(f"Commands dir: {commands_dir}") 

107 print() 

108 

109 s_adapted, s_skipped, s_errors = _process_skills(skills_dir, args.apply, args.quiet) 

110 c_adapted, c_skipped, c_errors = _process_commands( 

111 commands_dir, skills_dir, args.apply, args.quiet 

112 ) 

113 

114 adapted = s_adapted + c_adapted 

115 skipped = s_skipped + c_skipped 

116 errors = s_errors + c_errors 

117 

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

119 return 0 if errors == 0 else 1