refactor: modularize monolithic __init__.py into command-specific submodules#2461
Open
darion-yaphet wants to merge 31 commits intogithub:mainfrom
Open
refactor: modularize monolithic __init__.py into command-specific submodules#2461darion-yaphet wants to merge 31 commits intogithub:mainfrom
darion-yaphet wants to merge 31 commits intogithub:mainfrom
Conversation
Move StepTracker, get_key, select_with_arrows, BannerGroup, show_banner, BANNER, and TAGLINE from __init__.py into the new _ui.py module. Re-export all symbols from __init__.py to preserve the public API.
Moves handle_vscode_settings, merge_json_files, save_init_options, and load_init_options (plus INIT_OPTIONS_FILE constant) from __init__.py into the new _fs.py module; re-exports them via __init__.py import. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the four _locate_* functions from __init__.py into a dedicated AssetService class in _assets.py. Backward-compatible wrapper functions remain in __init__.py delegating to a module-level singleton.
Move git operations into a pure GitService class with zero console output. Backward-compatible wrappers in __init__.py retain Rich print calls.
Move version checking logic (get_installed_version, normalize_tag, is_newer, fetch_latest_tag) into a dedicated VersionService class in _version.py; replace the four original functions in __init__.py with thin backward-compatible wrappers. Remove now-dead InvalidVersion/Version imports from __init__.py.
Moves run_command, check_tool, _install_shared_infra, ensure_executable_scripts, ensure_constitution_from_template, and _get_skills_dir (plus CLAUDE_LOCAL_PATH/CLAUDE_NPM_LOCAL_PATH) out of __init__.py into a new _helpers.py module. Re-exports all symbols from __init__.py for backward compatibility. Removes now-unused top-level subprocess import. Updates test_check_tool.py patch targets to the canonical _helpers namespace.
- Move `init` command (~630 lines) from `__init__.py` to `commands/init.py` using the `register(app)` pattern to avoid circular imports - Move `AGENT_CONFIG`, `AI_ASSISTANT_ALIASES`, `AI_ASSISTANT_HELP`, `SCRIPT_TYPE_CHOICES`, `get_speckit_version`, `_parse_integration_options` to `_helpers.py`; re-export from `__init__.py` for backward compatibility - Fix `_get_skills_dir` in `_helpers.py` to use module-local `AGENT_CONFIG` instead of circular `from specify_cli import AGENT_CONFIG` - Use `sys.modules` lookup for `select_with_arrows` so existing test patches on `specify_cli.select_with_arrows` continue to work - `__init__.py` reduced from 4501 → 3734 lines
- Remove dead import _get_skills_dir from _helpers - Delete five one-line wrapper functions (_is_git_repo, _init_git_repo, _locate_bundled_extension/workflow/preset) and inline direct calls to _git_svc and _svc at all call sites - Remove redundant inline `import shutil as _shutil`; use top-level shutil - Promote BRANCH_NUMBERING_CHOICES to module-level _BRANCH_NUMBERING_CHOICES - Add -> str return type annotation on _build_integration_equivalent - Remove empty finally: pass block
Extracts integration_app, all @integration_app.command handlers, and helper functions (_read_integration_json, _write_integration_json, _remove_integration_json, _normalize_script_type, _resolve_script_type, _update_init_options_for_integration) from __init__.py into the focused commands/integration.py module. __init__.py re-exports the public symbols via a clean import block.
Extracts all preset_app and preset_catalog_app command handlers out of the monolithic __init__.py into src/specify_cli/commands/preset.py. Updates test patch target for _locate_bundled_preset to the new module path.
Extracted all extension_app and catalog_app command handlers and private helpers (_resolve_installed_extension, _resolve_catalog_extension, _print_extension_info, _locate_bundled_extension) from __init__.py into the new commands/extension.py module. Updated the single patched test target from specify_cli._locate_bundled_extension to specify_cli.commands.extension._locate_bundled_extension. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extracted all workflow_app and workflow_catalog_app command handlers from __init__.py into src/specify_cli/commands/workflow.py using the sub-typer pattern consistent with preset.py and extension.py. - Replaced 660 lines of inline handlers with a 3-line import + add_typer - Added module-level constants _SPECIFY_DIR and _WORKFLOWS_SUBDIR - All handlers carry -> None return type annotations - Lazy inline imports used for WorkflowEngine, WorkflowRegistry, etc. - No circular imports; __init__.py now ~340 lines (was 995)
Remove stdlib imports no longer used in the file body (zipfile, tempfile, shutil, json, shlex, yaml, os), unused Rich imports (Text, Live), dead _TOML_AGENTS constant, and slim down _ui/_helpers/_assets/_git/_version imports to only what is actually needed. Retain urllib.request/urllib.error and all backward-compat re-exports (select_with_arrows, _get_skills_dir, AI_ASSISTANT_ALIASES, AI_ASSISTANT_HELP, _install_shared_infra, _parse_integration_options, _fs helpers) that are patched or imported by the test suite or by internal sibling modules. All 1765 tests pass.
Resolved 60+ linting issues across the source code and test suite to ensure consistency following the modularization refactor. Key changes: - Cleaned up unused imports (F401) and redundant variable assignments (F841) in src/specify_cli/ and tests/. - Fixed ambiguous variable names (E741) by renaming 'l' to 'line' in list comprehensions within test files. - Removed extraneous f-string prefixes (F541) from static strings. - Standardized import ordering and resolved module-level import placement issues (E402) in src/specify_cli/__init__.py. - Split multiple imports on single lines (E401) to comply with PEP 8. - Restored essential service singletons (_svc, _git_svc, _ver_svc) in the main entry point required by wrapper functions. All checks passed via .
Restored several key constants and functions to the package root in that were removed during modularization. These exports are required by the existing test suite. Resolved names: - AI_ASSISTANT_ALIASES & AI_ASSISTANT_HELP (re-exported from ._helpers) - save_init_options, merge_json_files, & handle_vscode_settings (re-exported from ._fs) Fixed ImportErrors in: - tests/test_agent_config_consistency.py - tests/test_branch_numbering.py - tests/test_merge.py Verified that all 41 affected tests now pass.
… state schema - Introduce integration.json schema v1 with default_integration, installed_integrations, and integration_settings fields; migrate legacy v0 format on first read - Add 'integration install' multi-install support: allow multiple multi_install_safe integrations to coexist without --force - Add 'integration use' command to switch default integration without uninstall/reinstall - Update 'integration switch' to set default when target is already installed, or perform full uninstall/reinstall otherwise - Update 'integration uninstall' to handle multi-install state and refresh templates for the new default on default removal - Update 'integration upgrade' to skip template refresh when upgrading a non-default integration - Add 'Multi-install Safe' column to 'integration list' table - Enforce integration_state_schema version guard in list/install/etc. - Export _refresh_shared_templates, _parse_integration_options, select_with_arrows, and urllib from __init__ for test monkeypatching - Delegate _install_shared_infra to shared_infra.install_shared_infra to gain symlink-safe writes and atomic template updates - Write integration.json in schema v1 format during 'init' - Fix remove_catalog bool priority to fall back to yaml index - Fix extension/workflow catalog list to use is_dir() instead of exists() - Split long Rich console messages onto separate lines to prevent ANSI span wrapping in narrow terminal environments
- Remove unused urllib.error import; mark urllib.request with noqa F401 (required for test monkeypatching of specify_cli.urllib.request.urlopen) - Add noqa E402 to late imports that must follow function definitions - Remove unused yaml import from integration.py - Remove unused INTEGRATION_REGISTRY imports in uninstall/info/search - Remove unused IntegrationCatalogError imports in info/search - Remove unused default_key variable in integration_use - Remove extraneous f-string prefixes on string literals without placeholders
Path objects on Windows use backslashes, causing test assertions that check for forward-slash paths to fail. Call .as_posix() on every relative_to() result before printing to ensure consistent POSIX-style separators across platforms.
…indows The previous fix used relative_to().as_posix(), which silently fails when project_root and the resolved absolute path have different representations (e.g. short vs. long paths, symlinks) on Windows. Replace all user-facing path output with more robust alternatives: - Catalog 'Config saved to' messages: hardcode the known POSIX-relative path string directly (.specify/preset-catalogs.yml etc.) instead of computing it from Path objects at runtime - Catalog 'Config:' dim hints: same hardcoded approach - Uninstall skipped-file listing: use os.path.relpath() + replace(os.sep, '/') which handles resolved vs. unresolved path mismatches on all platforms
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.
Description
This PR addresses the technical debt in
src/specify_cli/__init__.pyby refactoring the monolithic entry point (previously >5,000 lines) into a clean, modular, and maintainable architecture.Key architectural improvements:
src/specify_cli/commands/(init.py,integration.py,preset.py,extension.py, andworkflow.py)._helpers.py,_fs.py,_ui.py,_git.py,_assets.py,_console.py, and_version.py.__init__.pycaused by multiple overlapping refactoring branches, successfully bridgingHEADwith the incoming modular command structures.__init__.pynow acts strictly as thetyperapplication entry point, handling only app configuration and sub-command registration.These changes are purely structural. The external CLI interface, command behaviors, and arguments remain fully backward compatible.
Testing
uv run specify --helpuv sync && uv run pytestAI Disclosure
Code refactoring and complex merge conflict resolutions in
src/specify_cli/__init__.pywere accomplished via a pair-programming session with an AI coding assistant (Antigravity). The AI helped to safely extract thousands of lines of monolithic logic into the new modular file structure (commands/*and_*internal services), utilizingsedand structural parsing to ensure no command implementations were lost during the cross-branch merges.