github/spec-kit
The system is a single Python CLI (`specify`, entrypoint `specify_cli:main`) built on Typer/Rich. The root app in `src/specify_cli/__init__.py` wires together command groups that each own a subpackage: init/check/version/self, integration, extension, preset, workflow, bundle, artifact, and event. All distributable content (templates, scripts, extensions, workflows, presets, catalogs) is bundled into the wheel via hatchling force-include as `core_pack`, with a repo-root fallback for source checkouts. State is stored inside each target project under `.specify/` (init-options.json, integration.json, extensions.yml, manifests, workflow run state). External integrations are limited to HTTPS downloads (GitHub API/releases, catalog JSON, extension archives) and subprocess invocation of agent CLIs.
- The console script `specify` maps to `specify_cli:main`; `main()` forces UTF-8 stdout/stderr on Windows then invokes the root Typer `app` defined in `src/specify_cli/__init__.py`.
- The root Typer app registers every command group centrally: `init` via `commands/init.py`, `check`/`version`/`self` locally, plus `extension`, `integration`, `event`, `preset`, `artifact`, `bundle`, and `workflow` groups imported from their `_commands.py`/`commands/*.py` modules and attached via `register(app)` calls.
- The wheel bundles all runtime assets into `specify_cli/core_pack/` via `[tool.hatch.build.targets.wheel.force-include]`: page templates, `templates/commands`, bash/powershell/python scripts, bundled extensions (git, agent-context, assess, bug), workflows (speckit, bugfix, assess), presets (lean, constitution-sync), and bundle catalog snapshots — making `specify init` work without network access.
- `_assets.py` is the stdlib-only base of the dependency graph: `_locate_core_pack()` finds the wheel-bundled `core_pack/` directory and `_repo_root()` falls back to the source checkout; `_locate_bundled_extension|workflow|preset` check core_pack first, then `extensions/`, `workflows/`, `presets/` under the repo root.
- `INTEGRATION_REGISTRY` in `integrations/__init__.py` is the single source of truth for AI-agent integrations; `_register_builtins()` instantiates and registers ~40 integrations (claude, copilot, gemini, codex, kilocode, cursor-agent, kiro-cli, generic, ...) in alphabetical order, with duplicate or empty keys rejected.
- Command-group layout is codified in `design/cli.md`: each real CLI command lives in `command_<name>.py`, private phases in `_command_<name>_<phase>.py`, group registration in `_commands.py`, and nested CLI namespaces (e.g. `extension catalog add`) map to matching subdirectories — `extensions/` is the reference implementation.
- The workflow subsystem pairs `workflows/engine.py` (WorkflowDefinition parsing, `validate_workflow`, WorkflowEngine orchestration, RunState persistence) with `workflows/__init__.py`'s `STEP_REGISTRY`, which registers 11 built-in step types: command, shell, prompt, gate, if, init, slot, switch, while, do-while, fan-out, fan-in.
- Authentication is opt-in and registry-based: `AUTH_REGISTRY` holds GitHubAuth and AzureDevOpsAuth providers; credentials are only sent when the user creates `~/.specify/auth.json` mapping hosts to providers, and `_github_http.py` restricts `Authorization` headers (from GITHUB_TOKEN/GH_TOKEN) to a fixed GITHUB_HOSTS allowlist.
- Project-scoped commands resolve the project root through `_require_specify_project()`, which requires a `.specify/` directory in the cwd unless the `SPECIFY_INIT_DIR` env var overrides it — this single chokepoint applies uniformly to integration, extension, preset, workflow, bundle, and artifact subcommands.
- The package targets Python >=3.11 with deps typer, click, rich, platformdirs, readchar, pyyaml, packaging, pathspec, json5; tests run under pytest (`testpaths = ["tests"]`) and CI exercises Linux/macOS/Windows on Python 3.13/3.14.
- Distribution is a PyPI wheel (`specify-cli`) typically installed via `uv tool install specify-cli`; releases are cut by pushing `v*` tags which trigger `.github/workflows/release.yml` to build notes and create the GitHub release.
- Self-version reporting uses `importlib.metadata.version("specify-cli")` first and falls back to reading `pyproject.toml` from the repo root, returning "unknown" when neither works.