CodeVetter · Repo Unpack · Contributor handoff
github/spec-kit
This repo is engineered for agent contributors: AGENTS.md gives a 5-step integration quickstart and design/cli.md defines a strict file-naming contract (`command_<name>.py`, `_command_<name>_<phase>.py`, `_commands.py`, mirrored tests). The safe-edit boundary is the registry/declarative layer — prefer adding a subpackage, extension.yml, preset.yml, or workflow.yml over touching the three giant domain modules. Non-negotiables: all installed files go through manifest recording, no `shell=True`, preserve root-level re-exports and `_commands` forwarders (tests monkeypatch them), honor the dual core_pack/repo-root asset lookup, and keep sh/ps/py script behavior identical.
- Read `AGENTS.md` first — it is the authoritative guide for adding integrations (base-class table, required class attrs, registry insertion, context-file ownership by the agent-context extension, test file naming), and `design/cli.md` before adding or moving any CLI command.
- Module naming contract: real CLI commands live in `command_<name>.py` (the only files that may register handlers), private phases in `_command_<name>_<phase>.py`, group apps/registration in `_commands.py`, nested CLI namespaces get matching directories — tests mirror under `tests/specify_cli/` as `test_command_<name>.py`.
- Build/test commands: `uv sync --extra test` then `uv run pytest`; lint with `uvx ruff check src tests`; markdown via markdownlint-cli2; CI matrix is ubuntu/windows/macos × Python 3.13/3.14 — keep tests passing on Windows (no POSIX-only assumptions).
- Never introduce `shell=True`, `os.system`, or `popen2` — ruff rules S602/S604/S605 are extended-selected specifically to make any deviation require a visible `# noqa`.
- Route every file an integration writes through `manifest.record_file()`/`record_existing()` in `setup()` — files not recorded are invisible to uninstall, and files written directly bypass the hash-check that protects user modifications.
- Preserve compatibility surfaces: root-package re-exports (`workflow_add`, `check_tool`, `load_init_options`, `_require_specify_project`, …) and `_commands.py` call-time forwarders exist because tests monkeypatch `specify_cli.<name>`; removing them breaks the suite without breaking imports.
- Asset lookups must handle both layouts: wheel installs have `specify_cli/core_pack/` (force-included at build time) while source checkouts/edits do not — always go through `_locate_core_pack()`/`_repo_root()`/`_locate_bundled_*` rather than `Path(__file__).parent` arithmetic (a past regression broke command discovery this way).
- `init-options.json` has tri-state semantics — missing file (legacy fallback to detect-all-agents), malformed/empty `ai` (fail closed, register nothing), valid string key — use `resolve_active_agent_for_registration`/`load_init_options`/`is_ai_skills_enabled` rather than reading the JSON ad hoc; a dangling symlink must fail closed, not fall back.
- Path safety helpers already exist — reuse them instead of rolling new checks: `_validate_rel_path`/`_ensure_safe_manifest_*` (manifest), `_ensure_safe_shared_directory`/`_ensure_safe_shared_destination`/`_write_shared_*` (shared_infra), `relative_extension_path_violation` (_utils), symlink-component checks in workflows/_commands.
- Every new integration needs `tests/integrations/test_integration_<key>.py` (hyphens→underscores in the filename); multi-install-safe integrations must use static unique dirs, and registry tests enforce those invariants — run the focused file plus `tests/test_agent_config_consistency.py`.
- Env-var surface an agent must know: `SPECIFY_INIT_DIR` retargets every project-scoped command; `SPECKIT_INTEGRATION_DEFAULT` changes the non-interactive init default; `SPECKIT_INTEGRATION_<KEY>_EXECUTABLE`/`_EXTRA_ARGS` and `SPECKIT_WORKFLOW_RUN_ID` exist — tests strip all `SPECIFY_*`/`SPECKIT_INTEGRATION_*` vars via conftest, so set them explicitly in tests.
- Command templates must stay tri-script: any `templates/commands/*.md` change that references a helper script needs behaviorally equivalent `sh`/`ps`/`py` entries in `scripts:` frontmatter and the parity tests (`test_*_python_parity.py`) pin the stdout contract (`FEATURE_DIR:`, `AVAILABLE_DOCS:`, `--json` shapes).