github/spec-kit
Inputs enter through Typer commands or content manifests (extension.yml, preset.yml, workflow.yml, bundle.yml, catalog JSON). `specify init` resolves an integration from `INTEGRATION_REGISTRY`, writes `.specify/init-options.json`, renders command templates with per-agent placeholders into the agent's config directory, and copies shared scripts/templates into `.specify/` — every written file hash-recorded in an `IntegrationManifest`. Extensions and presets layer on top via `CommandRegistrar` rendering and a priority-resolved template stack; hooks land in `.specify/extensions.yml`. Workflow runs persist resumable state under `.specify/workflows/runs/<run_id>/`. Outbound data is confined to HTTPS downloads with byte caps and optional SHA-256 pins, and subprocess calls to agent CLIs.
- `specify init` persists the user's choices to `.specify/init-options.json` via `save_init_options`; `load_init_options`/`is_ai_skills_enabled`/`resolve_active_agent_for_registration` read it back and distinguish missing-file (legacy fallback) from malformed-file (fail closed) via the `MISSING_INIT_OPTIONS_FILE` sentinel.
- Core command templates declare per-script-type helper invocations in `scripts:` frontmatter (`sh`, `ps`, `py` entries) and carry placeholders (`{SCRIPT}`, `$ARGUMENTS`, `__SPECKIT_COMMAND_<NAME>__`) that integrations resolve at install time through `process_template()` using the integration's `invoke_separator` (`.` for markdown agents, `-` for skills agents).
- Every file an integration installs is recorded with a SHA-256 in `.specify/integrations/<key>.manifest.json` via `IntegrationManifest.record_file`/`record_existing`; `teardown()` delegates to `manifest.uninstall()` which deletes only files whose current hash still matches (skipping user-modified files) unless `--force`.
- Shared infrastructure (`_install_shared_infra_impl` in `shared_infra.py`) copies `.specify/scripts/<variant>/` and `.specify/templates/` from core_pack or the repo root, resolves `__SPECKIT_COMMAND_*__` placeholders, tracks files in `speckit.manifest.json`, and writes a managed `.specify/.gitignore` covering `feature.json` and per-machine `extensions/*/local-config.yml`.
- Workflow run state is persisted under `.specify/workflows/runs/<run_id>/` as atomically-written `state.json` + `inputs.json` plus an appended `log.jsonl`; `RunState.load` validates `run_id` against a strict pattern before building the path, and `workflow resume` reloads the persisted workflow copy and merges new inputs over persisted ones.
- Extension installs parse `extension.yml` into an `ExtensionManifest` (validating `provides.commands` names against `^speckit\.<ns>\.<name>$`, hooks, events, templates, scripts), copy files via `ExtensionManager.install_from_directory`/`install_from_zip`, and merge `hooks` entries into `.specify/extensions.yml` through `register_hooks`.
- Remote content (catalog JSON, extension/preset/workflow archives) is fetched with hard byte ceilings — `MAX_DOWNLOAD_BYTES` 50 MiB, `MAX_JSON_METADATA_BYTES` 1 MiB, `MAX_JSON_CATALOG_BYTES` 8 MiB, per-entry and total zip caps — via `read_response_limited`/`safe_extract_archive`; catalog-declared `sha256` pins are verified with `hmac.compare_digest` before install.
- The events flow generates a self-contained `.specify/events.py` dispatcher plus native hook config per integration; the dispatcher resolves the event's command template and runs its script confined under the project root (`EVENT_SCRIPT_PATH_CONFINEMENT = True`), with `specify event run` as the fallback entry point that accepts a ≤1 MiB stdin payload.
- `CommandRegistrar` (agents.py) lazily derives `AGENT_CONFIGS` from `INTEGRATION_REGISTRY` `registrar_config` dicts (propagating `invoke_separator` and `dev_no_symlink`), then renders commands into each agent's directory/format for extensions and presets — so the registry is the single metadata source for both the integration layer and the registrar.
- Skills-mode installs branch on the persisted `ai_skills` flag: `resolve_active_skills_dir` maps the project to `<agent_folder>/skills` (with symlink/containment checks via `_ensure_safe_shared_directory`), special-cases `generic` (no static folder → disabled) and `kimi` (native-skills fallback when flag unset).
- The workflow engine resolves the dispatch integration by layered fallback — step `integration:` → workflow default → `.specify/integration.json` project default — with `"auto"` meaning the project-recorded integration, then `CommandStep.execute` builds the agent CLI argv via `build_exec_args` and streams output to the terminal.
- Catalog stacks (`CatalogStackBase` + per-domain subclasses) merge an ordered list of catalog sources, each entry carrying `url`, `priority`, and `install_allowed`; catalog entries from discovery-only catalogs are treated as untrusted — e.g. `_command_safe_id` refuses to interpolate non-`[a-z0-9-]` IDs into suggested shell commands.