refactor(cli): decompose the cli.py monolith into a cli/ package#85
Open
donalddellapietra wants to merge 8 commits into
Open
refactor(cli): decompose the cli.py monolith into a cli/ package#85donalddellapietra wants to merge 8 commits into
donalddellapietra wants to merge 8 commits into
Conversation
…hange) First step of decomposing the 7.5k-line CLI monolith. The module sponsio/cli.py becomes the sponsio/cli/ package: - cli.py -> cli/_monolith.py (verbatim; carved into commands/ + groups/ in following commits). - cli/__init__.py re-exports every command, group, and the internal helpers that other modules and tests import from 'sponsio.cli', so 'from sponsio.cli import X' is unchanged. - cli/__main__.py restores 'python -m sponsio.cli' (a package needs an explicit __main__; the plugin tests shell out via that form). - demo's repo_root now resolves via the sponsio package location instead of __file__ depth, so it survives the move. Command set identical (23 top-level + subgroups); 695 CLI tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the top-level 'cli' click.Group into its own module so command and group modules can register on it without importing the package root. _monolith now imports cli from app; no behavior change (23 commands, 695 CLI tests pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the two self-contained IDE/daemon groups out of the monolith into cli/groups/daemon.py and cli/groups/cursor.py. __init__ imports them for registration and re-exports the names. No behavior change (subcommands intact; CLI tests pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the skill group, skill_install, and the shared skill-install verification helpers (_packaged_skill_source, _verify_skill_install_target, _SKILL_TOOL_DIRS, _SkillInstallHealth, …) out of the monolith. doctor.py and the host-install path import the shared helpers from here now; __init__ re-exports them for back-compat. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the plugin group, all seven subcommands (init/install/show/append/ scan/prompt/guard), and the plugin-library helpers out of the monolith. Add cli/_shared.py for genuinely cross-module helpers and relocate _contract_guarantee there (used by plugin + several commands). host install imports _bootstrap_default_buckets and _install_one from the plugin module. No behavior change (7 subcommands intact; tests pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the host group + all subcommands (install/status/trace/list/migrate/ uninstall/guard) and host-lifecycle helpers out of the monolith. host imports its cross-module deps (skill packaged-source, plugin bucket bootstrap + _install_one, _shared _contract_guarantee); onboard now imports _resolve_runtime_mode from the host module. All 5 command groups now live in cli/groups/. Full suite: 2296 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Relocate _looks_like_sponsio_config, _resolve_entry, _parse_since, and _parse_existing_contracts (each used by 2+ commands, and _resolve_entry imported by eval_runner) into _shared.py alongside _contract_guarantee. __init__ re-exports _resolve_entry from there. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Final decomposition step: move the 18 top-level commands out of the monolith into one module each under cli/commands/ (demo, patterns, packs, validate, check, explain, replay, report, serve, scan, export, export_sessions, eval, init, doctor, onboard, mode, prompt). Command-local helpers travel with their command (scan's yaml filters, export's OTLP converters, mode's yaml patcher); cross-command coupling is explicit (onboard imports scan/mode helpers + host's mode resolver; export_sessions imports export's OTLP converters). _monolith.py is deleted. __init__ imports every command/group module (for registration) and re-exports the full public + internal API, so 'from sponsio.cli import X' is unchanged. main() now lives in __init__. Command set identical (23 top-level + subgroups); full suite 2296 passed; python -m sponsio.cli and the console script both work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Splits the single 7,542-line
sponsio/cli.pyinto asponsio/cli/package — one module per top-level command undercommands/, one per command group undergroups/, a root group inapp.py, and cross-command helpers in_shared.py. Pure restructuring: no behavior change.Type of change
Before → after
How compatibility is preserved
Many modules and tests do
from sponsio.cli import X— thecligroup, command callables (onboard,doctor,scan, …), and internal helpers (_resolve_entry,_patch_mode_in_yaml,_filter_invalid_contracts,_packaged_skill_source,_refresh_per_host_bundles, …).__init__re-exports every one of those from its new home, so no external import changed. Thesponsio.cli:mainconsole-script entry andpython -m sponsio.cliboth still work.Design notes
_shared→ commands.demo'srepo_rootwas computed from__file__depth (would break on the move) — now resolves via the package location; and a package needs__main__.pyforpython -m sponsio.cli(the plugin tests shell out via that form).onboardimportsscan/modehelpers + host's runtime-mode resolver;export_sessionsimportsexport's OTLP converters;hostinstall reusesplugin's bucket bootstrap andskill's packaged-source resolver.plugin.py(1154) andhost.py(1037) remain large — they're genuinely big groups (7 subcommands each); per-subcommand splitting is a possible follow-up.Test plan
Command set unchanged: 23 top-level commands; subgroups skill(1)/plugin(7)/host(7)/daemon(3)/cursor(2).
Checklist
pytest -vpasses locally (2296 passed).ruff check/ruff format --checkclean.🤖 Generated with Claude Code