Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
65ecaa9
Initial plan
Copilot Mar 9, 2026
2e8a4d6
feat(templates): add pluggable template system with packs, catalog, r…
Copilot Mar 9, 2026
6003a23
test(templates): add comprehensive unit tests for template pack system
Copilot Mar 9, 2026
abf4aeb
feat(presets): pluggable preset system with template/command override…
mnriem Mar 10, 2026
914a06a
Merge remote-tracking branch 'upstream/main' into pr-1787
mnriem Mar 10, 2026
35ced30
feat(presets): propagate command overrides to skills via init-options
mnriem Mar 10, 2026
445eefe
fix: address PR check failures (ruff F541, CodeQL URL substring)
mnriem Mar 10, 2026
52f137c
fix: address Copilot PR review comments
mnriem Mar 10, 2026
3ffef55
fix: narrow empty except blocks and add explanatory comments
mnriem Mar 10, 2026
da6e7d2
fix: address Copilot PR review comments (round 2)
mnriem Mar 10, 2026
1c143e6
fix: remove self-test from catalog.json (local-only preset)
mnriem Mar 10, 2026
6da1375
fix: address Copilot PR review comments (round 3)
mnriem Mar 10, 2026
f5f8311
fix: correct PresetError docstring from template to preset
mnriem Mar 10, 2026
d8bc72f
Merge remote-tracking branch 'upstream/main' into copilot/add-pluggab…
mnriem Mar 13, 2026
7259652
Removed CHANGELOG requirement
mnriem Mar 13, 2026
f7fbda5
Applying review recommendations
mnriem Mar 13, 2026
db66637
Applying review recommendations
mnriem Mar 13, 2026
1a0f8b1
Applying review recommendations
mnriem Mar 13, 2026
13a46dd
Applying review recommendations
mnriem Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ The toolkit supports multiple AI coding assistants, allowing teams to use their

---

## General practices

- Any changes to `__init__.py` for the Specify CLI require a version rev in `pyproject.toml` and addition of entries to `CHANGELOG.md`.

## Adding New Agent Support

This section explains how to add support for new AI agents/assistants to the Specify CLI. Use this guide as a reference when integrating new AI tools into the Spec-Driven Development workflow.
Expand Down
28 changes: 21 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ Recent changes to the Specify CLI and templates are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- feat(presets): Pluggable preset system with preset catalog and template resolver
- Preset manifest (`preset.yml`) with validation for artifact, command, and script types
- `PresetManifest`, `PresetRegistry`, `PresetManager`, `PresetCatalog`, `PresetResolver` classes in `src/specify_cli/presets.py`
- CLI commands: `specify preset search`, `specify preset add`, `specify preset list`, `specify preset remove`, `specify preset resolve`, `specify preset info`
- CLI commands: `specify preset catalog list`, `specify preset catalog add`, `specify preset catalog remove` for multi-catalog management
- `PresetCatalogEntry` dataclass and multi-catalog support mirroring the extension catalog system
- `--preset` option for `specify init` to install presets during initialization
- Priority-based preset resolution: presets with lower priority number win (`--priority` flag)
- `resolve_template()` / `Resolve-Template` helpers in bash and PowerShell common scripts
- Template resolution priority stack: overrides → presets → extensions → core
- Preset catalog files (`presets/catalog.json`, `presets/catalog.community.json`)
- Preset scaffold directory (`presets/scaffold/`)
- Scripts updated to use template resolution instead of hardcoded paths
- feat(presets): Preset command overrides now propagate to agent skills when `--ai-skills` was used during init
- feat: `specify init` persists CLI options to `.specify/init-options.json` for downstream operations
- feat(extensions): support `.extensionignore` to exclude files/folders during `specify extension add` (#1781)

## [0.2.1] - 2026-03-11

### Changed
Expand Down Expand Up @@ -51,13 +72,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
- fix: Split release process to sync pyproject.toml version with git tags (#1732)


## [Unreleased]

### Added

- feat(extensions): support `.extensionignore` to exclude files/folders during `specify extension add` (#1781)

## [0.2.0] - 2026-03-09

### Changed
Expand Down
157 changes: 157 additions & 0 deletions presets/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Preset System Architecture

This document describes the internal architecture of the preset system — how template resolution, command registration, and catalog management work under the hood.

For usage instructions, see [README.md](README.md).

## Template Resolution

When Spec Kit needs a template (e.g. `spec-template`), the `PresetResolver` walks a priority stack and returns the first match:

```mermaid
flowchart TD
A["resolve_template('spec-template')"] --> B{Override exists?}
B -- Yes --> C[".specify/templates/overrides/spec-template.md"]
B -- No --> D{Preset provides it?}
D -- Yes --> E[".specify/presets/‹preset-id›/templates/spec-template.md"]
D -- No --> F{Extension provides it?}
F -- Yes --> G[".specify/extensions/‹ext-id›/templates/spec-template.md"]
F -- No --> H[".specify/templates/spec-template.md"]
E -- "multiple presets?" --> I["lowest priority number wins"]
I --> E
style C fill:#4caf50,color:#fff
style E fill:#2196f3,color:#fff
style G fill:#ff9800,color:#fff
style H fill:#9e9e9e,color:#fff
```

| Priority | Source | Path | Use case |
|----------|--------|------|----------|
| 1 (highest) | Override | `.specify/templates/overrides/` | One-off project-local tweaks |
| 2 | Preset | `.specify/presets/<id>/templates/` | Shareable, stackable customizations |
| 3 | Extension | `.specify/extensions/<id>/templates/` | Extension-provided templates |
| 4 (lowest) | Core | `.specify/templates/` | Shipped defaults |

When multiple presets are installed, they're sorted by their `priority` field (lower number = higher precedence). This is set via `--priority` on `specify preset add`.

The resolution is implemented three times to ensure consistency:
- **Python**: `PresetResolver` in `src/specify_cli/presets.py`
- **Bash**: `resolve_template()` in `scripts/bash/common.sh`
- **PowerShell**: `Resolve-Template` in `scripts/powershell/common.ps1`

## Command Registration

When a preset is installed with `type: "command"` entries, the `PresetManager` registers them into all detected agent directories using the shared `CommandRegistrar` from `src/specify_cli/agents.py`.

```mermaid
flowchart TD
A["specify preset add my-preset"] --> B{Preset has type: command?}
B -- No --> Z["done (templates only)"]
B -- Yes --> C{Extension command?}
C -- "speckit.myext.cmd\n(3+ dot segments)" --> D{Extension installed?}
D -- No --> E["skip (extension not active)"]
D -- Yes --> F["register command"]
C -- "speckit.specify\n(core command)" --> F
F --> G["detect agent directories"]
G --> H[".claude/commands/"]
G --> I[".gemini/commands/"]
G --> J[".github/agents/"]
G --> K["... (17+ agents)"]
H --> L["write .md (Markdown format)"]
I --> M["write .toml (TOML format)"]
J --> N["write .agent.md + .prompt.md"]
style E fill:#ff5722,color:#fff
style L fill:#4caf50,color:#fff
style M fill:#4caf50,color:#fff
style N fill:#4caf50,color:#fff
```

### Extension safety check

Command names follow the pattern `speckit.<ext-id>.<cmd-name>`. When a command has 3+ dot segments, the system extracts the extension ID and checks if `.specify/extensions/<ext-id>/` exists. If the extension isn't installed, the command is skipped — preventing orphan files referencing non-existent extensions.

Core commands (e.g. `speckit.specify`, with only 2 segments) are always registered.

### Agent format rendering

The `CommandRegistrar` renders commands differently per agent:

| Agent | Format | Extension | Arg placeholder |
|-------|--------|-----------|-----------------|
| Claude, Cursor, opencode, Windsurf, etc. | Markdown | `.md` | `$ARGUMENTS` |
| Copilot | Markdown | `.agent.md` + `.prompt.md` | `$ARGUMENTS` |
| Gemini, Qwen, Tabnine | TOML | `.toml` | `{{args}}` |

### Cleanup on removal

When `specify preset remove` is called, the registered commands are read from the registry metadata and the corresponding files are deleted from each agent directory, including Copilot companion `.prompt.md` files.

## Catalog System

```mermaid
flowchart TD
A["specify preset search"] --> B["PresetCatalog.get_active_catalogs()"]
B --> C{SPECKIT_PRESET_CATALOG_URL set?}
C -- Yes --> D["single custom catalog"]
C -- No --> E{.specify/preset-catalogs.yml exists?}
E -- Yes --> F["project-level catalog stack"]
E -- No --> G{"~/.specify/preset-catalogs.yml exists?"}
G -- Yes --> H["user-level catalog stack"]
G -- No --> I["built-in defaults"]
I --> J["default (install allowed)"]
I --> K["community (discovery only)"]
style D fill:#ff9800,color:#fff
style F fill:#2196f3,color:#fff
style H fill:#2196f3,color:#fff
style J fill:#4caf50,color:#fff
style K fill:#9e9e9e,color:#fff
```

Catalogs are fetched with a 1-hour cache (per-URL, SHA256-hashed cache files). Each catalog entry has a `priority` (for merge ordering) and `install_allowed` flag.

## Repository Layout

```
presets/
├── ARCHITECTURE.md # This file
├── PUBLISHING.md # Guide for submitting presets to the catalog
├── README.md # User guide
├── catalog.json # Official preset catalog
├── catalog.community.json # Community preset catalog
├── scaffold/ # Scaffold for creating new presets
│ ├── preset.yml # Example manifest
│ ├── README.md # Guide for customizing the scaffold
│ ├── commands/
│ │ ├── speckit.specify.md # Core command override example
│ │ └── speckit.myext.myextcmd.md # Extension command override example
│ └── templates/
│ ├── spec-template.md # Core template override example
│ └── myext-template.md # Extension template override example
└── self-test/ # Self-test preset (overrides all core templates)
├── preset.yml
├── commands/
│ └── speckit.specify.md
└── templates/
├── spec-template.md
├── plan-template.md
├── tasks-template.md
├── checklist-template.md
├── constitution-template.md
└── agent-file-template.md
```

## Module Structure

```
src/specify_cli/
├── agents.py # CommandRegistrar — shared infrastructure for writing
│ # command files to agent directories
├── presets.py # PresetManifest, PresetRegistry, PresetManager,
│ # PresetCatalog, PresetCatalogEntry, PresetResolver
└── __init__.py # CLI commands: specify preset list/add/remove/search/
# resolve/info, specify preset catalog list/add/remove
```
Loading
Loading