# Spec Kit — Administrator & Developer Guide

## 1. Architecture Overview

Spec Kit is a Python-based CLI toolkit that implements Spec-Driven Development (SDD) by scaffolding project structures, generating slash commands for AI coding agents, and orchestrating a multi-phase development workflow. The architecture is built around four composable subsystems: **integrations** (agent adapters), **extensions** (plugin commands and hooks), **presets** (template customization layers), and **workflows** (multi-step automation).

The system is designed as a local-first CLI tool with no server component, no database, and no runtime daemon. All state is stored in the filesystem under `.specify/` within the project directory.

```text
┌─────────────────────────────────────────────────────────┐
│                    specify CLI (Typer)                    │
│  init │ check │ upgrade │ version                        │
├────────────┬──────────────┬─────────────┬───────────────┤
│ integration│  extension   │   preset    │   workflow    │
│  subsystem │  subsystem   │  subsystem  │   engine      │
├────────────┴──────────────┴─────────────┴───────────────┤
│              CommandRegistrar (agents.py)                 │
│  Renders commands into agent-native formats:             │
│  Markdown │ TOML │ YAML │ Skills                         │
├─────────────────────────────────────────────────────────┤
│                    Filesystem State                       │
│  .specify/  │  .claude/  │  .github/  │  .cursorrules/  │
└─────────────────────────────────────────────────────────┘
```

### Component Inventory

| Component | Path | Role |
|---|---|---|
| CLI entry point | `src/specify_cli/__init__.py` | Typer app with all commands (5,300+ lines) |
| Command registrar | `src/specify_cli/agents.py` | Renders commands to agent-native formats |
| Extension system | `src/specify_cli/extensions.py` | Extension lifecycle, catalog, hooks, config |
| Preset system | `src/specify_cli/presets.py` | Preset lifecycle, catalog, template resolution |
| Integration base | `src/specify_cli/integrations/base.py` | Abstract base classes for agent integrations |
| Integration catalog | `src/specify_cli/integrations/catalog.py` | Integration discovery and metadata |
| Integration manifest | `src/specify_cli/integrations/manifest.py` | File tracking with SHA-256 hashes |
| Workflow engine | `src/specify_cli/workflows/engine.py` | Step execution, state machine, expression eval |
| Workflow base | `src/specify_cli/workflows/base.py` | Workflow manifest parsing |
| Workflow catalog | `src/specify_cli/workflows/catalog.py` | Workflow discovery |
| Shell scripts (Bash) | `scripts/bash/` | `check-prerequisites.sh`, `common.sh`, `create-new-feature.sh`, `setup-plan.sh` |
| Shell scripts (PowerShell) | `scripts/powershell/` | Same scripts in PowerShell format |
| Core templates | `templates/` | `spec-template.md`, `plan-template.md`, `tasks-template.md`, `checklist-template.md`, `constitution-template.md` |
| VS Code settings | `templates/vscode-settings.json` | Copilot prompt file recommendations and script auto-approve |

### Technology Stack

| Layer | Technology |
|---|---|
| Language | Python 3.11+ |
| CLI framework | Typer (Click-based) |
| Terminal UI | Rich (panels, tables, trees, live display) |
| Keyboard input | readchar (cross-platform) |
| JSON parsing | json5 (JSONC-compatible) |
| YAML parsing | PyYAML |
| Version comparison | packaging |
| Platform paths | platformdirs |
| Package manager | uv / pip |
| Build system | Hatch (hatchling) |

### Data Flow: Full SDD Cycle

```text
specify init --integration claude
    │
    ├── Check prerequisites (git, claude CLI)
    ├── Create .specify/ directory structure
    ├── Copy bundled templates and scripts
    ├── Install integration (register commands via CommandRegistrar)
    ├── Auto-install Git extension (if git available)
    ├── Initialize git repo (unless --no-git)
    ├── Copy constitution template to .specify/memory/
    └── Save init-options.json

User runs /speckit.specify in Claude Code
    │
    ├── AI reads spec-template.md (resolved through preset stack)
    ├── Scripts: check-prerequisites.sh --paths-only
    ├── Creates feature branch (Git extension hook: before_specify)
    ├── Generates specs/<branch>/spec.md
    └── Auto-commit (Git extension hook: after_specify, if configured)

/speckit.plan → /speckit.tasks → /speckit.implement
    │
    └── Each phase: check prerequisites → read templates → generate artifact → optional hooks
```

> [!NOTE]
> The workflow engine (`workflows/engine.py`) can automate this entire cycle with review gates at each step. The built-in `speckit` workflow requires an integration from `copilot`, `claude`, or `gemini`.

## 2. System Requirements

| Requirement | Version | Required | Notes |
|---|---|---|---|
| Python | ≥3.11 | Yes | Runtime for the CLI |
| `uv` | Latest | Recommended | Package management and tool installation |
| Git | Any recent | No | Required for the Git extension and branching features |
| AI agent CLI | Varies | At least one | Claude Code, Copilot (VS Code), Gemini CLI, etc. |

### Python Dependencies

| Package | Purpose |
|---|---|
| `typer` | CLI framework (Click-based, with type hints) |
| `rich` | Terminal formatting (panels, tables, progress trees) |
| `platformdirs` | Cross-platform config/data directory resolution |
| `readchar` | Cross-platform keyboard input (arrow key selection) |
| `json5` | JSONC parsing (comments and trailing commas) |
| `pyyaml` | YAML parsing for extensions, presets, workflows, configs |
| `packaging` | PEP 440 version parsing and comparison |

> [!IMPORTANT]
> These dependencies are declared in the script header of `__init__.py` and in `pyproject.toml`. They are installed automatically when using `uv tool install specify-cli`.

## 3. Configuration Reference

### Config Files

| File | Format | Location | Purpose |
|---|---|---|---|
| `init-options.json` | JSON | `.specify/` | Records CLI flags used during `specify init` |
| `speckit.manifest.json` | JSON | `.specify/` | Tracks all files installed by spec-kit with SHA-256 hashes |
| `extension.yml` | YAML | `.specify/extensions/<id>/` | Extension manifest (schema, commands, hooks, config) |
| `preset.yml` | YAML | `.specify/presets/<id>/` | Preset manifest (schema, templates, composition) |
| `git-config.yml` | YAML | `.specify/extensions/git/` | Git extension configuration |
| `catalog-config.json` | JSON | `.specify/extensions/` | Custom extension catalog sources |
| `preset-catalog-config.json` | JSON | `.specify/presets/` | Custom preset catalog sources |
| `registry.json` | JSON | `.specify/extensions/` | Extension registry (installed extensions metadata) |
| `registry.json` | JSON | `.specify/presets/` | Preset registry (installed presets metadata) |
| `vscode-settings.json` | JSON | `.vscode/settings.json` | VS Code settings (merged, not overwritten) |

### Environment Variables

| Variable | Type | Default | Description | Required |
|---|---|---|---|---|
| `SPECKIT_CATALOG_URL` | String | GitHub raw URL | Override the built-in extension catalog URL | No |
| `SPECKIT_PRESET_CATALOG_URL` | String | GitHub raw URL | Override the built-in preset catalog URL | No |

### CLI Flags — `specify init`

| Flag | Type | Default | Description |
|---|---|---|---|
| `--integration` | String | None (interactive) | AI agent integration to activate |
| `--integration-options` | String | None | Options string for the integration |
| `--script` | `sh` \| `ps` | None (interactive) | Script type for shell scripts |
| `--preset` | String | None | Preset to install during init |
| `--branch-numbering` | `sequential` \| `timestamp` | `sequential` | Branch numbering strategy |
| `--here` | Flag | False | Init in current directory |
| `--force` | Flag | False | Overwrite existing files |
| `--no-git` | Flag | False | Skip git init |
| `--ai-skills` | Flag | False | Install as agent skills |
| `-V` / `--version` | Flag | — | Show version and exit |

### Git Extension Configuration (`git-config.yml`)

```yaml
# Branch numbering strategy: "sequential" (001, 002, ...) or "timestamp" (YYYYMMDD-HHMMSS)
branch_numbering: sequential

# Commit message used by `git commit` during repository initialization
init_commit_message: "[Spec Kit] Initial commit"

# Auto-commit before/after core commands
auto_commit:
  default: false
  before_clarify:
    enabled: false
    message: "[Spec Kit] Save progress before clarification"
  before_plan:
    enabled: false
    message: "[Spec Kit] Save progress before planning"
  after_specify:
    enabled: false
    message: "[Spec Kit] Add specification"
  after_plan:
    enabled: false
    message: "[Spec Kit] Add implementation plan"
  after_implement:
    enabled: false
    message: "[Spec Kit] Implementation progress"
  # ... (18 hook points total: before/after for 9 core commands)
```

> [!TIP]
> Set `auto_commit.default: true` to enable automatic commits for all commands, then selectively disable specific ones. Each hook point can be individually configured with a custom commit message.

### Extension Manifest Schema (`extension.yml`)

```yaml
schema_version: "1.0"

extension:
  id: "my-extension"            # Lowercase alphanumeric with hyphens
  name: "My Extension"
  version: "1.0.0"              # Semantic versioning
  description: "Brief description"
  author: "Your Name"
  repository: "https://github.com/..."
  license: "MIT"

requires:
  speckit_version: ">=0.2.0"    # PEP 440 version specifier
  tools:
    - name: "example-tool"
      version: ">=1.0.0"
      required: true

provides:
  commands:
    - name: "speckit.my-extension.example"  # Must follow speckit.<ext>.<cmd> pattern
      file: "commands/example.md"
      description: "Example command"
      aliases: ["speckit.my-extension.ex"]  # Optional aliases
  config:
    - name: "my-config.yml"
      template: "config-template.yml"
      required: false

hooks:
  after_tasks:
    command: "speckit.my-extension.example"
    optional: true              # true = prompt user; false = auto-execute
    prompt: "Run example command?"
    description: "Hook description"

tags:
  - "example"
```

> [!IMPORTANT]
> Extension IDs must be lowercase alphanumeric with hyphens only. Command names must follow the pattern `speckit.<extension-id>.<command-name>`. Aliases must use the same namespace. Commands that conflict with core or other installed extension commands will be rejected.

### Preset Manifest Schema (`preset.yml`)

```yaml
schema_version: "1.0"

preset:
  id: "my-preset"
  name: "My Preset"
  version: "1.0.0"
  description: "Brief description"
  author: "Your Name"
  repository: "https://github.com/..."
  license: "MIT"

requires:
  speckit_version: ">=0.1.0"

provides:
  templates:
    - type: "template"          # "template", "command", or "script"
      name: "spec-template"
      file: "templates/spec-template.md"
      description: "Custom spec template"
      replaces: "spec-template"
      strategy: "replace"       # "replace" (default), "prepend", "append", "wrap"

tags:
  - "example"
```

**Composition strategies:**

| Strategy | Behavior |
|---|---|
| `replace` | Fully replaces the lower-priority template (default) |
| `prepend` | Places content BEFORE the lower-priority template |
| `append` | Places content AFTER the lower-priority template |
| `wrap` | Uses `{CORE_TEMPLATE}` placeholder; replaced with lower-priority content |

> [!NOTE]
> Scripts only support `replace` and `wrap` strategies. The `wrap` strategy for scripts uses `$CORE_SCRIPT` as the placeholder.

## 4. Deployment & Scaling

Spec Kit is a local CLI tool — there is no server deployment. Distribution is handled through PyPI.

### Package Distribution

The package is built with **Hatch** (hatchling build backend). The `pyproject.toml` configures:

- Package name: `specify-cli`
- Entry point: `specify = "specify_cli:main"`
- Build includes `src/specify_cli/` with a `core_pack/` directory containing bundled templates, scripts, extensions, presets, and workflows

### Wheel Layout

```text
specify_cli/
├── __init__.py          # CLI entry point (5,300+ lines)
├── agents.py            # CommandRegistrar
├── extensions.py        # Extension system
├── presets.py           # Preset system
├── integrations/        # 27+ agent integrations
│   ├── base.py
│   ├── catalog.py
│   ├── manifest.py
│   └── <agent>/__init__.py (one per integration)
├── workflows/           # Workflow engine
│   ├── engine.py
│   ├── base.py
│   ├── catalog.py
│   ├── expressions.py
│   └── steps/           # Step types (command, gate, shell, prompt, etc.)
└── core_pack/           # Bundled assets (templates, scripts, extensions, presets, workflows)
```

> [!NOTE]
> The `core_pack/` directory is created at build time by hatchling's `force-include` configuration. It is not present in source checkouts or editable installs. The code falls back to repo-relative paths in those cases.

## 5. Operations

### Catalog Infrastructure

Spec Kit uses JSON catalogs hosted on GitHub for extension, preset, and workflow discovery:

| Catalog | URL | Purpose |
|---|---|---|
| Extension (official) | `github/spec-kit/main/extensions/catalog.json` | Bundled extensions (git, selftest) |
| Extension (community) | `github/spec-kit/main/extensions/catalog.community.json` | Community extensions (50+) |
| Preset (official) | `github/spec-kit/main/presets/catalog.json` | Bundled presets (lean) |
| Preset (community) | `github/spec-kit/main/presets/catalog.community.json` | Community presets (11+) |
| Workflow (official) | `github/spec-kit/main/workflows/catalog.json` | Bundled workflows (speckit) |
| Workflow (community) | `github/spec-kit/main/workflows/catalog.community.json` | Community workflows (empty) |

Catalogs are fetched via HTTPS (HTTP only allowed for localhost). The cache is stored locally and can be cleared with `specify extension catalog clear` or equivalent preset commands.

> [!TIP]
> Organizations can host private catalogs and configure them via `catalog-config.json` in `.specify/extensions/` or `.specify/presets/`. Each catalog entry supports `url`, `name`, `priority`, `install_allowed`, and `description` fields.

### File Integrity Tracking

The `IntegrationManifest` system (in `integrations/manifest.py`) tracks every file installed by spec-kit:

- Files are recorded with SHA-256 hashes
- On uninstall, only unmodified files are removed
- Modified files are preserved (unless `--force` is used)
- Empty parent directories are cleaned up after file removal
- Path traversal attacks are blocked (no `../` or absolute paths)
- Symlinks are treated as modified files (skipped without `--force`)

> [!NOTE]
> The manifest is stored at `.specify/speckit.manifest.json`. Each integration and extension maintains its own manifest for clean uninstall behavior.

### Version Checking

The `specify upgrade` command checks the GitHub Releases API (`https://api.github.com/repos/github/spec-kit/releases/latest`) for newer versions. Version comparison uses the `packaging` library for PEP 440 compliance.

## 6. Performance

Spec Kit is a scaffolding tool — performance characteristics are dominated by I/O (file copying, git operations) rather than computation.

**Key performance notes:**

- The `init` command copies bundled assets from the wheel's `core_pack/` directory, avoiding network requests
- Extension/preset installation from catalogs requires one HTTPS fetch per catalog, with local caching
- The CommandRegistrar renders commands into agent-native formats at install time, not at command execution time
- The workflow engine executes steps sequentially with optional gate pauses
- Template resolution (`PresetResolver`) walks the priority stack once per resolve call, merging composition layers

> [!TIP]
> For large teams, consider pre-populating `.specify/` directories in project templates or monorepo scaffolds to avoid per-developer initialization costs.

## 7. Security

### Catalog URL Validation

All catalog URLs must use HTTPS. HTTP is only permitted for localhost (development/testing). This is enforced by `ValidationError` in both `ExtensionCatalog` and `PresetCatalog`.

```python
if parsed.scheme != "https":
    if parsed.scheme == "http" and parsed.hostname in ("localhost", "127.0.0.1"):
        pass  # Allowed for local development
    else:
        raise ValidationError(f"Catalog URL must use HTTPS (got {parsed.scheme}://)")
```

### ZIP Archive Safety

Extension and preset installation from ZIP files includes path traversal protection:

- All archive members are checked for `..` path components
- Absolute paths in ZIP entries are rejected
- The extraction uses a safe path validation before writing

```python
if '..' in member or os.path.isabs(member):
    raise ValidationError(f"Unsafe path in ZIP archive: {member} (potential path traversal)")
```

### Extension Command Namespace Isolation

Extensions must use their own namespace for commands: `speckit.<extension-id>.<command>`. The system rejects:

- Commands that conflict with core command namespaces
- Commands that conflict with other installed extensions
- Duplicate command names or aliases within a manifest

### Integration Manifest Integrity

The manifest system prevents:

- Recording files outside the project root (path traversal via `../`)
- Recording files with absolute paths
- Uninstalling files that have been modified (hash mismatch)
- Symlink-based attacks (symlinks are treated as modified)

> [!WARNING]
> Extension hooks can execute arbitrary commands within the AI agent context. Review extension manifests before installation, especially hooks with `optional: false` (auto-execute without prompting).

### Version Compatibility

Extensions and presets declare a `requires.speckit_version` constraint using PEP 440 version specifiers. Installation is blocked if the running spec-kit version doesn't satisfy the constraint:

```text
Extension requires spec-kit >=0.7.0, but 0.6.2 is installed.
Upgrade spec-kit with: uv tool install specify-cli --force
```

> [!CAUTION]
> The `--force` flag on `extension remove` and `integration uninstall` deletes files even if they've been modified. Use with care in shared repositories.

## 8. Developer Internals

### Repository Structure

```text
github/spec-kit/
├── src/specify_cli/            # Main package
│   ├── __init__.py             # CLI commands (5,300+ lines, Typer app)
│   ├── agents.py               # CommandRegistrar — multi-format command renderer
│   ├── extensions.py           # Extension system (2,600+ lines)
│   ├── presets.py              # Preset system (3,000+ lines)
│   ├── integrations/           # 27+ agent integrations
│   │   ├── __init__.py         # INTEGRATION_REGISTRY population
│   │   ├── base.py             # IntegrationBase, MarkdownIntegration, TomlIntegration, SkillsIntegration
│   │   ├── catalog.py          # IntegrationCatalogEntry, IntegrationDescriptor
│   │   ├── manifest.py         # IntegrationManifest (file tracking + SHA-256)
│   │   ├── claude/__init__.py  # Claude Code integration
│   │   ├── copilot/__init__.py # GitHub Copilot integration
│   │   └── ...                 # One module per integration
│   └── workflows/
│       ├── __init__.py
│       ├── base.py             # WorkflowManifest parsing
│       ├── catalog.py          # Workflow discovery
│       ├── engine.py           # WorkflowEngine — step execution
│       ├── expressions.py      # Template expression evaluation
│       └── steps/              # Step type implementations
│           ├── command/        # Command step (run a slash command)
│           ├── gate/           # Gate step (approve/reject)
│           ├── shell/          # Shell step
│           ├── prompt/         # Prompt step
│           ├── if_then/        # Conditional branching
│           ├── switch/         # Multi-branch conditional
│           ├── while_loop/     # While loop
│           ├── do_while/       # Do-while loop
│           ├── fan_out/        # Parallel fan-out
│           └── fan_in/         # Parallel fan-in (join)
├── extensions/
│   ├── git/                    # Bundled Git extension
│   ├── selftest/               # Bundled self-test extension
│   └── template/               # Extension development template
├── presets/
│   ├── lean/                   # Bundled Lean preset
│   ├── scaffold/               # Preset development scaffold
│   └── self-test/              # Bundled self-test preset
├── workflows/
│   └── speckit/                # Bundled Full SDD Cycle workflow
├── integrations/               # Integration catalog JSON files
├── templates/                  # Core command templates
├── scripts/
│   ├── bash/                   # Bash helper scripts
│   └── powershell/             # PowerShell helper scripts
├── tests/
│   ├── integrations/           # Integration tests (39 files, 264K chars)
│   ├── extensions/             # Extension tests
│   ├── hooks/                  # Hook test fixtures
│   ├── test_workflows.py
│   ├── test_presets.py
│   ├── test_extensions.py
│   └── ...
├── docs/                       # DocFX documentation source
├── media/                      # Logos, screenshots, GIFs
├── newsletters/                # Community newsletters
├── pyproject.toml              # Package configuration
├── AGENTS.md                   # Agent context file
├── CITATION.cff                # Citation metadata
└── .zenodo.json                # Zenodo deposit metadata
```

### Architecture Deep Dive

#### CommandRegistrar (`agents.py`)

The `CommandRegistrar` is the bridge between Spec Kit's template system and the diverse formats required by AI coding agents. It renders a single command definition into multiple agent-native formats:

- **Markdown**: Standard `.md` files with YAML frontmatter (Claude, Copilot agent mode, Windsurf, most agents)
- **TOML**: `.toml` files with `description` and `prompt` fields (Gemini, Tabnine)
- **YAML**: `.yaml` files with `title` and `prompt` fields (Goose)
- **Skills**: Directory-based layout with `SKILL.md` files containing structured frontmatter (Claude skills, Copilot skills, Cursor, Codex, Kimi, Trae, Agy)

Key methods:

| Method | Purpose |
|---|---|
| `parse_frontmatter()` | Extract YAML frontmatter from markdown content |
| `render_frontmatter()` | Serialize frontmatter dict back to YAML |
| `render_markdown_command()` | Render for Markdown-format agents |
| `render_toml_command()` | Render for TOML-format agents |
| `render_yaml_command()` | Render for YAML-format agents |
| `render_skill_command()` | Render for Skills-format agents |
| `register_commands()` | Write command files to agent directory |
| `register_commands_for_all_agents()` | Register across all detected agent directories |
| `unregister_commands()` | Remove previously registered command files |
| `write_copilot_prompt()` | Write companion `.prompt.md` files for Copilot |

The registrar also handles agent-specific post-processing:

- **Claude**: Injects `disableModelInvocation: false` and hook command notes into skill frontmatter
- **Copilot**: Injects `mode: agent` into skill frontmatter
- **Cursor**: Upserts `.mdc` frontmatter with `alwaysApply: false`
- **Forge**: Reformats command names from dotted to hyphenated (e.g., `speckit.specify` → `speckit-specify`)

#### Integration System (`integrations/`)

Each integration is a Python class inheriting from one of four base classes:

| Base Class | Format | Agents |
|---|---|---|
| `MarkdownIntegration` | `.md` with frontmatter | Windsurf, Amp, Auggie, Bob, CodeBuddy, iFlow, Junie, Kilocode, Kiro, opencode, Pi, Qoder, Qwen, Roo, SHAI, Vibe |
| `TomlIntegration` | `.toml` files | Gemini, Tabnine |
| `YamlIntegration` | `.yaml` files | Goose |
| `SkillsIntegration` | Skill directories | Claude, Codex, Cursor, Kimi, Trae, Agy |

Special integrations with custom logic:

- **`copilot`**: Supports both agent mode (`.md` files with companion `.prompt.md`) and skills mode (directory-based). Manages `.vscode/settings.json` merging
- **`claude`**: Skills layout with `disableModelInvocation` flag, BOM stripping for context files, argument hints in frontmatter
- **`forge`**: Custom command name formatting (dotted → hyphenated), parameter placeholder support
- **`generic`**: Requires `--commands-dir` option; no agent config folder assumed

The `INTEGRATION_REGISTRY` is populated at import time by scanning all integration modules. Each integration's `key` must match its CLI tool name.

#### Extension System (`extensions.py`)

The extension system is the largest module (2,600+ lines) and provides:

**Core classes:**

| Class | Purpose |
|---|---|
| `ExtensionManifest` | Parses and validates `extension.yml` |
| `ExtensionRegistry` | JSON-backed registry of installed extensions |
| `ExtensionManager` | Install, remove, list, compatibility checking |
| `ExtensionCatalog` | Fetch/cache/search catalogs, download ZIPs |
| `CommandRegistrar` | Extension-specific command registration |
| `ConfigManager` | Extension config file management |
| `HookExecutor` | Event-driven hook system |

**Validation rules enforced by `ExtensionManifest`:**

- Schema version must be `"1.0"`
- Extension ID: lowercase alphanumeric with hyphens only
- Version: valid semantic version
- Must provide at least one command or hook
- Command names must follow `speckit.<ext-id>.<cmd>` pattern
- Aliases must use the same extension namespace
- No conflict with core command namespaces
- No conflict with other installed extensions' commands

**Hook lifecycle:**

The `HookExecutor` supports these event points:

```text
before_constitution, after_constitution
before_specify, after_specify
before_clarify, after_clarify
before_plan, after_plan
before_tasks, after_tasks
before_implement, after_implement
before_checklist, after_checklist
before_analyze, after_analyze
before_taskstoissues, after_taskstoissues
```

Hooks with `optional: true` prompt the user before execution. Hooks with `optional: false` execute automatically.

#### Preset System (`presets.py`)

The preset system (3,000+ lines) mirrors the extension system architecture but adds template composition:

**Core classes:**

| Class | Purpose |
|---|---|
| `PresetManifest` | Parses and validates `preset.yml` |
| `PresetRegistry` | JSON-backed registry of installed presets |
| `PresetManager` | Install, remove, list, compatibility checking |
| `PresetCatalog` | Fetch/cache/search catalogs, download ZIPs |
| `PresetResolver` | Multi-layer template resolution with composition |

**Template resolution order (highest to lowest priority):**

1. Installed presets (by priority number, lowest = highest priority)
2. Extension-provided commands (via manifest)
3. Core templates (bundled in `.specify/templates/`)

**Composition execution in `PresetResolver.resolve_content()`:**

1. Collect all layers for the requested template
2. Start with the lowest-priority layer's content
3. Walk up the priority stack, applying each layer's strategy:
   - `replace`: discard everything below, use this layer
   - `prepend`: concatenate this layer + lower content
   - `append`: concatenate lower content + this layer
   - `wrap`: replace `{CORE_TEMPLATE}` in this layer with lower content

#### Workflow Engine (`workflows/`)

The workflow engine supports YAML-defined automation with these step types:

| Step Type | Module | Purpose |
|---|---|---|
| `command` | `steps/command/` | Execute a spec-kit slash command |
| `gate` | `steps/gate/` | Pause for human review (approve/reject) |
| `shell` | `steps/shell/` | Execute a shell command |
| `prompt` | `steps/prompt/` | Prompt the user for input |
| `if_then` | `steps/if_then/` | Conditional branching |
| `switch` | `steps/switch/` | Multi-branch conditional |
| `while_loop` | `steps/while_loop/` | While loop |
| `do_while` | `steps/do_while/` | Do-while loop |
| `fan_out` | `steps/fan_out/` | Parallel execution |
| `fan_in` | `steps/fan_in/` | Join parallel branches |

The `expressions.py` module handles template expression evaluation (e.g., `{{ inputs.spec }}`, `{{ steps.specify.output }}`).

### Module Reference

#### `src/specify_cli/__init__.py` (5,300+ lines)

The monolithic CLI module contains all Typer commands organized into groups:

| Command Group | Commands |
|---|---|
| Top-level | `init`, `check`, `version`, `upgrade` |
| `integration` | `list`, `install`, `uninstall`, `switch`, `upgrade` |
| `preset` | `list`, `add`, `remove`, `search`, `resolve`, `info`, `set-priority`, `enable`, `disable` |
| `preset catalog` | `list`, `add`, `remove` |
| `extension` | `list`, `add`, `remove`, `search`, `info`, `update`, `enable`, `disable`, `set-priority` |
| `extension catalog` | `list`, `add`, `remove` |
| `workflow` | `run`, `resume`, `status`, `list`, `add`, `remove`, `search`, `info` |
| `workflow catalog` | `list`, `add`, `remove` |

Key utility functions:

| Function | Purpose |
|---|---|
| `_locate_core_pack()` | Find bundled assets in wheel or source checkout |
| `_locate_bundled_extension()` | Find a bundled extension by ID |
| `_locate_bundled_preset()` | Find a bundled preset by ID |
| `_locate_bundled_workflow()` | Find a bundled workflow by ID |
| `_install_shared_infra()` | Install scripts and templates with manifest tracking |
| `ensure_executable_scripts()` | Set POSIX execute bits on `.sh` files |
| `ensure_constitution_from_template()` | Copy constitution template to memory directory |
| `check_tool()` | Check if a tool is installed (special handling for Claude local paths) |
| `select_with_arrows()` | Interactive arrow-key selection UI using Rich Live |
| `merge_json_files()` | Polite deep merge for JSON/JSONC files |
| `save_init_options()` / `load_init_options()` | Persist and restore init configuration |

#### `src/specify_cli/agents.py`

The `CommandRegistrar` class (620+ lines) with two distinct implementations:

1. **Core CommandRegistrar** (`agents.py`): Used during `specify init` to register core command templates
2. **Extension CommandRegistrar** (`extensions.py:1412`): Extends the core registrar for extension command registration

Both share `parse_frontmatter()` and `render_frontmatter()` but differ in how they handle agent-specific rendering.

#### `src/specify_cli/extensions.py` (2,600+ lines)

Major classes and their line counts:

| Class | Start Line | Purpose |
|---|---|---|
| `ExtensionManifest` | 119 | Manifest parsing with full validation |
| `ExtensionRegistry` | 360 | JSON-backed installed extension tracking |
| `ExtensionManager` | 597 | Install/remove lifecycle |
| `CommandRegistrar` | 1412 | Extension command registration |
| `ExtensionCatalog` | 1502 | Catalog fetch/cache/search/download |
| `ConfigManager` | 2026 | Extension config file management |
| `HookExecutor` | 2225 | Event-driven hook system |

#### `src/specify_cli/presets.py` (3,000+ lines)

Major classes and their line counts:

| Class | Start Line | Purpose |
|---|---|---|
| `PresetManifest` | 117 | Manifest parsing with template type and strategy validation |
| `PresetRegistry` | 296 | JSON-backed installed preset tracking |
| `PresetManager` | 527 | Install/remove lifecycle |
| `PresetCatalog` | 1786 | Catalog fetch/cache/search/download |
| `PresetResolver` | 2307 | Multi-layer template resolution with composition |

### Code Patterns and Conventions

**File operations:** Atomic writes via `tempfile.NamedTemporaryFile` + `os.replace()` for JSON files. Existing file permissions and ownership are preserved where possible.

**Error handling:** Custom exception hierarchies per subsystem:
- Extensions: `ExtensionError` → `ValidationError`, `CompatibilityError`
- Presets: `PresetError` → `PresetValidationError`, `PresetCompatibilityError`

**Registry pattern:** Both extensions and presets use a JSON-backed registry with add/remove/update/restore/get/list operations. The registry persists to `.specify/<subsystem>/registry.json`.

**Manifest pattern:** `IntegrationManifest` uses SHA-256 hashing for file change detection. Files are recorded at install time and verified at uninstall.

**Catalog pattern:** Both subsystems use the same catalog architecture: HTTPS fetch → JSON parse → local cache → search/filter. Catalogs support priority ordering and multi-source configuration.

**Version checking:** Uses `packaging.version.Version` for PEP 440 compliance. The `version_satisfies()` function handles specifier ranges like `>=0.7.0,<1.0.0`.

**CLI argument handling:** Typer with Click-based commands. The `--integration-options` flag is parsed as a shell-style string using `shlex`.

### Testing Strategy

The test suite (39 integration test files, 264K chars) is organized by subsystem:

| Test Area | Files | Focus |
|---|---|---|
| `tests/integrations/` | 39 | All 27 integrations with base class test mixins |
| `tests/extensions/` | 1 | Git extension tests |
| `tests/test_workflows.py` | 1 | Workflow engine tests |
| `tests/test_presets.py` | 1 | Preset system tests |
| `tests/test_extensions.py` | 1 | Extension system tests |
| `tests/test_cli_version.py` | 1 | Version command tests |
| `tests/test_check_tool.py` | 1 | Tool detection tests |
| `tests/test_merge.py` | 1 | JSON merge tests |
| `tests/test_upgrade.py` | 1 | Upgrade command tests |
| `tests/test_branch_numbering.py` | 1 | Branch naming tests |
| `tests/test_registrar_path_traversal.py` | 1 | Security: path traversal prevention |
| `tests/test_agent_config_consistency.py` | 1 | Config alignment tests |
| `tests/test_timestamp_branches.py` | 1 | Timestamp branch format tests |
| `tests/test_extension_skills.py` | 1 | Extension skills rendering tests |

**Test mixins for integrations:**

- `MarkdownIntegrationTests` — 20+ tests for Markdown-format agents
- `SkillsIntegrationTests` — 25+ tests for Skills-format agents
- `TomlIntegrationTests` — 20+ tests for TOML-format agents
- `YamlIntegrationTests` — 15+ tests for YAML-format agents

Each integration's test class inherits the appropriate mixin and runs the full suite against that specific agent adapter.

### Potential Improvements

Based on the codebase analysis:

1. **`__init__.py` is 5,300+ lines** — a single monolithic file containing all CLI commands. Splitting into `cli_init.py`, `cli_integration.py`, `cli_extension.py`, `cli_preset.py`, `cli_workflow.py` would improve maintainability.

2. **No formal plugin interface for step types** — workflow step types are discovered by module name convention. A registry pattern (like integrations use) would make this more extensible.

3. **Duplicate catalog logic** — `ExtensionCatalog` and `PresetCatalog` share nearly identical URL validation, fetch, cache, and search logic. A base `Catalog` class would reduce duplication.

4. **No dependency file detected** — the AST extractor found 0 dependency files, suggesting `pyproject.toml` parsing isn't being picked up. Dependencies are declared in the script header comment block.

5. **Community catalog contribution workflow** — currently requires manual PR to add JSON entries. A CLI command like `specify extension publish` could automate validation and PR creation.

### Contributing

The project uses:

- **License:** MIT
- **Build system:** Hatch (hatchling)
- **Test framework:** pytest with `tmp_path` fixtures
- **Linting:** markdownlint-cli2 (configured in `.markdownlint-cli2.jsonc`)
- **Citation:** CITATION.cff and .zenodo.json for academic referencing
- **Documentation:** DocFX (build with `cd docs && docfx docfx.json --serve`)

**To develop locally:**

```bash
git clone https://github.com/github/spec-kit.git
cd spec-kit
uv sync
uv run pytest
```

**Extension development:**

1. Copy `extensions/template/` to a new directory
2. Edit `extension.yml` with your extension ID, commands, and hooks
3. Create command files in `commands/`
4. Test locally with `specify extension add <id> --dev`
5. Publish to GitHub and submit a PR to `extensions/catalog.community.json`

**Preset development:**

1. Copy `presets/scaffold/` to a new directory
2. Edit `preset.yml` with your preset ID and templates
3. Create template files with optional composition strategies
4. Test locally with `specify preset add <id> --dev`
5. Publish and submit a PR to `presets/catalog.community.json`

## 9. Integration Points

### Git Integration (Bundled Extension)

The Git extension is auto-installed during `specify init` when Git is detected. It provides:

- **Feature branch creation** with sequential (001, 002, …) or timestamp (YYYYMMDD-HHMMSS) numbering
- **18 lifecycle hooks** (before/after for 9 core commands)
- **Configurable auto-commit** with per-command messages
- **Branch validation** and remote detection
- **Configuration** via `.specify/extensions/git/git-config.yml`

### VS Code Integration

When Copilot is the selected integration, Spec Kit manages `.vscode/settings.json`:

```json
{
    "chat.promptFilesRecommendations": {
        "speckit.constitution": true,
        "speckit.specify": true,
        "speckit.plan": true,
        "speckit.tasks": true,
        "speckit.implement": true
    },
    "chat.tools.terminal.autoApprove": {
        ".specify/scripts/bash/": true,
        ".specify/scripts/powershell/": true
    }
}
```

The merge is "polite" — existing keys are never overwritten, only new keys are added.

### Agent Context Files

Each integration writes to a context file that the AI agent reads:

| Integration | Context File |
|---|---|
| Claude | `CLAUDE.md` |
| Copilot | `.github/copilot-instructions.md` |
| Generic | `AGENTS.md` |
| Others | Agent-specific (e.g., `.cursorrules`, `.goose/config.yaml`) |

A managed section delimited by `<!-- SPECKIT START -->` / `<!-- SPECKIT END -->` is upserted into these files. BOM characters are stripped during processing.

### Shell Script Integration

The bundled scripts in `.specify/scripts/` provide project-level utility:

| Script | Purpose |
|---|---|
| `check-prerequisites.sh` / `.ps1` | Validate feature directory, spec, plan, tasks existence |
| `common.sh` / `.ps1` | Shared functions: find repo root, resolve feature paths, JSON escape |
| `create-new-feature.sh` / `.ps1` | Create a new feature branch and directory structure |
| `setup-plan.sh` / `.ps1` | Set up the planning phase prerequisites |

Scripts support `--json`, `--require-tasks`, `--include-tasks`, and `--paths-only` flags.

> [!NOTE]
> On POSIX systems, `ensure_executable_scripts()` automatically sets execute bits on `.sh` files during init. On Windows, this is a no-op.

---

_Generated May 2026 from github/spec-kit at 6bf4ebb. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
