Skip to content

refactor: move extension command handlers to extensions/_commands.py (PR-7/8)#3014

Open
darion-yaphet wants to merge 2 commits into
github:mainfrom
darion-yaphet:refactor/split-init-pr7
Open

refactor: move extension command handlers to extensions/_commands.py (PR-7/8)#3014
darion-yaphet wants to merge 2 commits into
github:mainfrom
darion-yaphet:refactor/split-init-pr7

Conversation

@darion-yaphet

Copy link
Copy Markdown
Contributor

Summary

PR-7 of the 8-part effort to break up the specify_cli/__init__.py monolith. Continues the domain-dir layout established in PR-5 (integrations/_commands.py) and PR-6 (presets/_commands.py).

This PR moves the extension * and catalog * command handlers out of __init__.py. Because extensions was a flat module (unlike the already-package presets/integrations), it is first converted to a package.

Changes

  • extensions.pyextensions/__init__.py — pure rename (99% identical). The module's intra-file relative imports are bumped from .x to ..x, since they reference root-package siblings (.._init_options, ..catalogs, ..agents, ..integrations, and from .. import for load_init_options, _print_cli_warning, AGENT_CONFIG, DEFAULT_SKILLS_DIR, _get_skills_dir).
  • New extensions/_commands.py — holds extension_app, catalog_app, all 12 command handlers (list/add/remove/search/info/update/enable/disable/set-priority + catalog list/add/remove), the private helpers (_resolve_installed_extension, _resolve_catalog_extension, _print_extension_info), and a register(app) entry point.
  • __init__.py drops ~1444 lines (3511 → 2067). The extension command group is re-attached via register(app), preserving the CLI surface.

Monkeypatch compatibility

Root helpers (_require_specify_project, _locate_bundled_extension, load_init_options, _display_project_path) are reached through thin shims in _commands.py that re-fetch from the parent package at call time. This keeps existing specify_cli.<helper> monkeypatch targets working with no test changes.

Verification

  • extension/catalog command tree loads and all --help surfaces respond.
  • Full test suite failure set is identical before and after this change (82 pre-existing environment failures unrelated to the refactor; 0 new, 0 fixed-by-accident).

No behavior change — pure structural move.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the Specify CLI by extracting specify extension * and specify extension catalog * command handlers out of the specify_cli/__init__.py monolith into a dedicated extensions/_commands.py module, while converting extensions from a flat module into a package to support the domain-dir layout.

Changes:

  • Introduces src/specify_cli/extensions/_commands.py containing the Typer apps (extension, catalog) and all related command handlers, plus a register(app) entry point.
  • Converts extensions.py into extensions/__init__.py and updates intra-package imports accordingly.
  • Re-attaches the extension command group from specify_cli/__init__.py via _register_extension_cmds(app) to preserve the CLI surface.
Show a summary per file
File Description
src/specify_cli/extensions/_commands.py New home for extension / catalog Typer apps and all extension-related CLI handlers (registered via register(app)).
src/specify_cli/extensions/init.py Converts extensions into a package and adjusts relative imports to continue referencing root-package siblings.
src/specify_cli/init.py Removes inline extension command definitions and registers the new extensions command module to keep the same CLI entry points.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 3

Comment thread src/specify_cli/extensions/_commands.py Outdated
Comment thread src/specify_cli/extensions/_commands.py Outdated
Comment thread src/specify_cli/extensions/_commands.py Outdated

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback and resolve conflicts

…(PR-7/8)

Convert the flat extensions.py module into an extensions/ package and
extract all extension_app and catalog_app command handlers plus their
private helpers (_resolve_installed_extension, _resolve_catalog_extension,
_print_extension_info) out of __init__.py into the new
extensions/_commands.py, mirroring the domain-dir layout used for
presets/_commands.py (PR-6) and integrations/_commands.py (PR-5).

- extensions.py -> extensions/__init__.py (pure rename, 99%); intra-module
  relative imports bumped from `.x` to `..x` since they reference root
  siblings.
- Root helpers (_require_specify_project, _locate_bundled_extension,
  load_init_options, _display_project_path) are reached through thin shims
  that re-fetch from the parent package at call time, so test
  monkeypatching of specify_cli.<helper> keeps working unchanged.
- __init__.py drops ~1444 lines (3511 -> 2067); CLI surface preserved via
  register(app).

No behavior change. Full suite failure set is identical before/after
(82 pre-existing env failures, 0 new).
…ry the catalog

- extension add --from: sanitize the extension label before building the
  download filename so "../" path separators can no longer escape the
  downloads dir and overwrite arbitrary files
- extension list --available/--all: actually query the catalog and list
  uninstalled extensions (filtering out installed IDs), instead of only
  printing a static install hint that contradicted the CLI help and docs
@darion-yaphet darion-yaphet force-pushed the refactor/split-init-pr7 branch from 50756f8 to ddcc165 Compare June 17, 2026 13:54
@darion-yaphet

Copy link
Copy Markdown
Contributor Author

fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

output_name = _AgentReg._compute_output_name(agent_name, cmd_name, agent_config)
cmd_file = commands_dir / f"{output_name}{agent_config['extension']}"
if cmd_file.exists():
backup_cmd_path = backup_commands_dir / agent_name / cmd_file.name
})

config["catalogs"] = catalogs
config_path.write_text(yaml.dump(config, default_flow_style=False, sort_keys=False, allow_unicode=True), encoding="utf-8")
raise typer.Exit(1)

config["catalogs"] = catalogs
config_path.write_text(yaml.dump(config, default_flow_style=False, sort_keys=False, allow_unicode=True), encoding="utf-8")
Comment on lines +191 to +195
# Default (no flags) lists installed; --all also lists installed.
# --available alone lists only catalog extensions, not installed.
show_installed = all_extensions or not available
show_available = available or all_extensions

@mnriem

mnriem commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Please address Copilot feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants