mirror of
https://github.com/github/spec-kit.git
synced 2026-07-06 14:01:01 +08:00
* Rewrite AGENTS.md for integration subpackage architecture Replaces the old AGENT_CONFIG dict-based 7-step process with documentation reflecting the integration subpackage architecture shipped in #1924. Removed: Supported Agents table, old step-by-step guide referencing AGENT_CONFIG/release scripts/case statements, Agent Categories lists, Directory Conventions section, Important Design Decisions section. Kept: About Spec Kit and Specify, Command File Formats, Argument Patterns, Devcontainer section. Added: Architecture overview, decision tree for base class selection, configure/register/scripts/test/override steps with real code examples from existing integrations (Windsurf, Gemini, Codex, Copilot). Agent-Logs-Url: https://github.com/github/spec-kit/sessions/71b25c53-7d0c-492a-9503-f40a437d5ece Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * Fix JSONC comment syntax in devcontainer example Agent-Logs-Url: https://github.com/github/spec-kit/sessions/71b25c53-7d0c-492a-9503-f40a437d5ece Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * docs(AGENTS.md): address Copilot PR review comments - Clarify that integrations are registered by _register_builtins() in __init__.py, not self-registered at import time - Scope the key-must-match-executable rule to CLI-based integrations (requires_cli: True); IDE-based integrations use canonical identifiers - Replace <commands_dir> placeholder in test snippet with a concrete example path (.windsurf/workflows/) - Document that hyphens in keys become underscores in test filenames (e.g. cursor-agent -> test_integration_cursor_agent.py) - Note that the argument placeholder is integration-specific (registrar_config["args"]); add Forge's {{parameters}} as an example - Apply consistency fixes to Required fields table, Key design rule callout, and Common Pitfalls #1 * docs(AGENTS.md): clarify scripts path uses Python-safe package_dir not key The scripts step previously referenced src/specify_cli/integrations/<key>/scripts/ but for hyphenated keys the actual directory is underscored (e.g. kiro-cli -> kiro_cli/). Rename the placeholder to <package_dir> and add a note explaining: - <package_dir> matches <key> for non-hyphenated keys - <package_dir> uses underscores for hyphenated keys (e.g. kiro-cli -> kiro_cli/) - IntegrationBase.key always retains the original hyphenated value Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3054946896 * docs(AGENTS.md): use <key_with_underscores> in pytest example command The pytest command previously used <key> as a placeholder, but test filenames always use underscores even for hyphenated keys. This was internally inconsistent since the preceding sentence already explained the hyphen→underscore mapping. Switch to <key_with_underscores> to match the actual filename on disk. Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3054962863 * docs(AGENTS.md): use <package_dir> in step 2 subpackage path The path src/specify_cli/integrations/<key>/__init__.py was inaccurate for hyphenated keys (e.g. kiro-cli lives in kiro_cli/, not kiro-cli/). Rename the placeholder to <package_dir>, define it inline (hyphens become underscores), and note that IntegrationBase.key always retains the original hyphenated value. Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3058050583 * docs(AGENTS.md): qualify 'single source of truth' to Python metadata only The registry is only authoritative for Python integration metadata. Context-update dispatcher scripts (bash + PowerShell) still require explicit per-agent cases and maintain their own supported-agent lists until they are migrated to registry-based dispatch. Tighten the claim to avoid misleading contributors into skipping the script updates. Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083090261 * docs(AGENTS.md): mention ValidateSet update in PowerShell dispatcher step The update-agent-context.ps1 script has a [ValidateSet(...)] on the AgentType parameter. Without adding the new key to that list, the script rejects the argument before reaching Update-SpecificAgent. Add this as an explicit step alongside the switch case and Update-AllExistingAgents. Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083217694 * fix(integrations): sort codebuddy before codex in _register_builtins() Both the import list and the _register() call list had codex before codebuddy, violating the alphabetical ordering that AGENTS.md documents. Swap them so the file matches the documented convention. Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083341590 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
367 lines
14 KiB
Markdown
367 lines
14 KiB
Markdown
# AGENTS.md
|
|
|
|
## About Spec Kit and Specify
|
|
|
|
**GitHub Spec Kit** is a comprehensive toolkit for implementing Spec-Driven Development (SDD) - a methodology that emphasizes creating clear specifications before implementation. The toolkit includes templates, scripts, and workflows that guide development teams through a structured approach to building software.
|
|
|
|
**Specify CLI** is the command-line interface that bootstraps projects with the Spec Kit framework. It sets up the necessary directory structures, templates, and AI agent integrations to support the Spec-Driven Development workflow.
|
|
|
|
The toolkit supports multiple AI coding assistants, allowing teams to use their preferred tools while maintaining consistent project structure and development practices.
|
|
|
|
---
|
|
|
|
## Integration Architecture
|
|
|
|
Each AI agent is a self-contained **integration subpackage** under `src/specify_cli/integrations/<key>/`. The subpackage exposes a single class that declares all metadata and inherits setup/teardown logic from a base class. Built-in integrations are then instantiated and added to the global `INTEGRATION_REGISTRY` by `src/specify_cli/integrations/__init__.py` via `_register_builtins()`.
|
|
|
|
```
|
|
src/specify_cli/integrations/
|
|
├── __init__.py # INTEGRATION_REGISTRY + _register_builtins()
|
|
├── base.py # IntegrationBase, MarkdownIntegration, TomlIntegration, SkillsIntegration
|
|
├── manifest.py # IntegrationManifest (file tracking)
|
|
├── claude/ # Example: SkillsIntegration subclass
|
|
│ ├── __init__.py # ClaudeIntegration class
|
|
│ └── scripts/ # Thin wrapper scripts
|
|
│ ├── update-context.sh
|
|
│ └── update-context.ps1
|
|
├── gemini/ # Example: TomlIntegration subclass
|
|
│ ├── __init__.py
|
|
│ └── scripts/
|
|
├── windsurf/ # Example: MarkdownIntegration subclass
|
|
│ ├── __init__.py
|
|
│ └── scripts/
|
|
├── copilot/ # Example: IntegrationBase subclass (custom setup)
|
|
│ ├── __init__.py
|
|
│ └── scripts/
|
|
└── ... # One subpackage per supported agent
|
|
```
|
|
|
|
The registry is the **single source of truth for Python integration metadata**. Supported agents, their directories, formats, and capabilities are derived from the integration classes for the Python integration layer. However, context-update behavior still requires explicit cases in the shared dispatcher scripts (`scripts/bash/update-agent-context.sh` and `scripts/powershell/update-agent-context.ps1`), which currently maintain their own supported-agent lists and agent-key→context-file mappings until they are migrated to registry-based dispatch.
|
|
|
|
---
|
|
|
|
## Adding a New Integration
|
|
|
|
### 1. Choose a base class
|
|
|
|
| Your agent needs… | Subclass |
|
|
|---|---|
|
|
| Standard markdown commands (`.md`) | `MarkdownIntegration` |
|
|
| TOML-format commands (`.toml`) | `TomlIntegration` |
|
|
| Skill directories (`speckit-<name>/SKILL.md`) | `SkillsIntegration` |
|
|
| Fully custom output (companion files, settings merge, etc.) | `IntegrationBase` directly |
|
|
|
|
Most agents only need `MarkdownIntegration` — a minimal subclass with zero method overrides.
|
|
|
|
### 2. Create the subpackage
|
|
|
|
Create `src/specify_cli/integrations/<package_dir>/__init__.py`, where `<package_dir>` is the Python-safe directory name derived from `<key>`: use the key as-is when it contains no hyphens (e.g., key `"gemini"` → `gemini/`), or replace hyphens with underscores when it does (e.g., key `"kiro-cli"` → `kiro_cli/`). The `IntegrationBase.key` class attribute always retains the original hyphenated value, since that is what the CLI and registry use. For CLI-based integrations (`requires_cli: True`), the `key` should match the actual CLI tool name (the executable users install and run) so CLI checks can resolve it correctly. For IDE-based integrations (`requires_cli: False`), use the canonical integration identifier instead.
|
|
|
|
**Minimal example — Markdown agent (Windsurf):**
|
|
|
|
```python
|
|
"""Windsurf IDE integration."""
|
|
|
|
from ..base import MarkdownIntegration
|
|
|
|
|
|
class WindsurfIntegration(MarkdownIntegration):
|
|
key = "windsurf"
|
|
config = {
|
|
"name": "Windsurf",
|
|
"folder": ".windsurf/",
|
|
"commands_subdir": "workflows",
|
|
"install_url": None,
|
|
"requires_cli": False,
|
|
}
|
|
registrar_config = {
|
|
"dir": ".windsurf/workflows",
|
|
"format": "markdown",
|
|
"args": "$ARGUMENTS",
|
|
"extension": ".md",
|
|
}
|
|
context_file = ".windsurf/rules/specify-rules.md"
|
|
```
|
|
|
|
**TOML agent (Gemini):**
|
|
|
|
```python
|
|
"""Gemini CLI integration."""
|
|
|
|
from ..base import TomlIntegration
|
|
|
|
|
|
class GeminiIntegration(TomlIntegration):
|
|
key = "gemini"
|
|
config = {
|
|
"name": "Gemini CLI",
|
|
"folder": ".gemini/",
|
|
"commands_subdir": "commands",
|
|
"install_url": "https://github.com/google-gemini/gemini-cli",
|
|
"requires_cli": True,
|
|
}
|
|
registrar_config = {
|
|
"dir": ".gemini/commands",
|
|
"format": "toml",
|
|
"args": "{{args}}",
|
|
"extension": ".toml",
|
|
}
|
|
context_file = "GEMINI.md"
|
|
```
|
|
|
|
**Skills agent (Codex):**
|
|
|
|
```python
|
|
"""Codex CLI integration — skills-based agent."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from ..base import IntegrationOption, SkillsIntegration
|
|
|
|
|
|
class CodexIntegration(SkillsIntegration):
|
|
key = "codex"
|
|
config = {
|
|
"name": "Codex CLI",
|
|
"folder": ".agents/",
|
|
"commands_subdir": "skills",
|
|
"install_url": "https://github.com/openai/codex",
|
|
"requires_cli": True,
|
|
}
|
|
registrar_config = {
|
|
"dir": ".agents/skills",
|
|
"format": "markdown",
|
|
"args": "$ARGUMENTS",
|
|
"extension": "/SKILL.md",
|
|
}
|
|
context_file = "AGENTS.md"
|
|
|
|
@classmethod
|
|
def options(cls) -> list[IntegrationOption]:
|
|
return [
|
|
IntegrationOption(
|
|
"--skills",
|
|
is_flag=True,
|
|
default=True,
|
|
help="Install as agent skills (default for Codex)",
|
|
),
|
|
]
|
|
```
|
|
|
|
#### Required fields
|
|
|
|
| Field | Location | Purpose |
|
|
|---|---|---|
|
|
| `key` | Class attribute | Unique identifier; for CLI-based integrations (`requires_cli: True`), must match the CLI executable name |
|
|
| `config` | Class attribute (dict) | Agent metadata: `name`, `folder`, `commands_subdir`, `install_url`, `requires_cli` |
|
|
| `registrar_config` | Class attribute (dict) | Command output config: `dir`, `format`, `args` placeholder, file `extension` |
|
|
| `context_file` | Class attribute (str or None) | Path to agent context/instructions file (e.g., `"CLAUDE.md"`, `".github/copilot-instructions.md"`) |
|
|
|
|
**Key design rule:** For CLI-based integrations (`requires_cli: True`), `key` must be the actual executable name (e.g., `"cursor-agent"` not `"cursor"`). This ensures `shutil.which(key)` works for CLI-tool checks without special-case mappings. IDE-based integrations (`requires_cli: False`) should use their canonical identifier (e.g., `"windsurf"`, `"copilot"`).
|
|
|
|
### 3. Register it
|
|
|
|
In `src/specify_cli/integrations/__init__.py`, add one import and one `_register()` call inside `_register_builtins()`. Both lists are alphabetical:
|
|
|
|
```python
|
|
def _register_builtins() -> None:
|
|
# -- Imports (alphabetical) -------------------------------------------
|
|
from .claude import ClaudeIntegration
|
|
# ...
|
|
from .newagent import NewAgentIntegration # ← add import
|
|
# ...
|
|
|
|
# -- Registration (alphabetical) --------------------------------------
|
|
_register(ClaudeIntegration())
|
|
# ...
|
|
_register(NewAgentIntegration()) # ← add registration
|
|
# ...
|
|
```
|
|
|
|
### 4. Add scripts
|
|
|
|
Create two thin wrapper scripts in `src/specify_cli/integrations/<package_dir>/scripts/` that delegate to the shared context-update scripts. Each is ~25 lines of boilerplate.
|
|
|
|
> **Note on `<package_dir>` vs `<key>`:** `<package_dir>` is the Python-safe directory name for your integration — it matches `<key>` exactly when the key contains no hyphens (e.g., key `"gemini"` → `gemini/`), but uses underscores when it does (e.g., key `"kiro-cli"` → `kiro_cli/`). The `IntegrationBase.key` class attribute always retains the original hyphenated value (e.g., `key = "kiro-cli"`), since that is what the CLI and registry use.
|
|
|
|
**`update-context.sh`:**
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
# update-context.sh — <Agent Name> integration: create/update <context_file>
|
|
set -euo pipefail
|
|
|
|
_script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
_root="$_script_dir"
|
|
while [ "$_root" != "/" ] && [ ! -d "$_root/.specify" ]; do _root="$(dirname "$_root")"; done
|
|
if [ -z "${REPO_ROOT:-}" ]; then
|
|
if [ -d "$_root/.specify" ]; then
|
|
REPO_ROOT="$_root"
|
|
else
|
|
git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
if [ -n "$git_root" ] && [ -d "$git_root/.specify" ]; then
|
|
REPO_ROOT="$git_root"
|
|
else
|
|
REPO_ROOT="$_root"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
exec "$REPO_ROOT/.specify/scripts/bash/update-agent-context.sh" <key>
|
|
```
|
|
|
|
**`update-context.ps1`:**
|
|
|
|
```powershell
|
|
# update-context.ps1 — <Agent Name> integration: create/update <context_file>
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
|
$repoRoot = try { git rev-parse --show-toplevel 2>$null } catch { $null }
|
|
if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot '.specify'))) {
|
|
$repoRoot = $scriptDir
|
|
$fsRoot = [System.IO.Path]::GetPathRoot($repoRoot)
|
|
while ($repoRoot -and $repoRoot -ne $fsRoot -and -not (Test-Path (Join-Path $repoRoot '.specify'))) {
|
|
$repoRoot = Split-Path -Parent $repoRoot
|
|
}
|
|
}
|
|
|
|
& "$repoRoot/.specify/scripts/powershell/update-agent-context.ps1" -AgentType <key>
|
|
```
|
|
|
|
Replace `<key>` with your integration key and `<Agent Name>` / `<context_file>` with the appropriate values.
|
|
|
|
You must also add the agent to the shared context-update scripts so the shared dispatcher recognises the new key:
|
|
|
|
- **`scripts/bash/update-agent-context.sh`** — add a file-path variable and a case in `update_specific_agent()`.
|
|
- **`scripts/powershell/update-agent-context.ps1`** — add a file-path variable, add the new key to the `AgentType` parameter's `[ValidateSet(...)]`, add a switch case in `Update-SpecificAgent`, and add an entry in `Update-AllExistingAgents`.
|
|
|
|
### 5. Test it
|
|
|
|
```bash
|
|
# Install into a test project
|
|
specify init my-project --integration <key>
|
|
|
|
# Verify files were created in the commands directory configured by
|
|
# config["folder"] + config["commands_subdir"] (for example, .windsurf/workflows/)
|
|
ls -R my-project/.windsurf/workflows/
|
|
|
|
# Uninstall cleanly
|
|
cd my-project && specify integration uninstall <key>
|
|
```
|
|
|
|
Each integration also has a dedicated test file at `tests/integrations/test_integration_<key>.py`. Note that hyphens in the key are replaced with underscores in the filename (e.g., key `cursor-agent` → `test_integration_cursor_agent.py`, key `kiro-cli` → `test_integration_kiro_cli.py`). Run it with:
|
|
|
|
```bash
|
|
pytest tests/integrations/test_integration_<key_with_underscores>.py -v
|
|
```
|
|
|
|
### 6. Optional overrides
|
|
|
|
The base classes handle most work automatically. Override only when the agent deviates from standard patterns:
|
|
|
|
| Override | When to use | Example |
|
|
|---|---|---|
|
|
| `command_filename(template_name)` | Custom file naming or extension | Copilot → `speckit.{name}.agent.md` |
|
|
| `options()` | Integration-specific CLI flags via `--integration-options` | Codex → `--skills` flag |
|
|
| `setup()` | Custom install logic (companion files, settings merge) | Copilot → `.agent.md` + `.prompt.md` + `.vscode/settings.json` |
|
|
| `teardown()` | Custom uninstall logic | Rarely needed; base handles manifest-tracked files |
|
|
|
|
**Example — Copilot (fully custom `setup`):**
|
|
|
|
Copilot extends `IntegrationBase` directly because it creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
|
|
|
|
### 7. Update Devcontainer files (Optional)
|
|
|
|
For agents that have VS Code extensions or require CLI installation, update the devcontainer configuration files:
|
|
|
|
#### VS Code Extension-based Agents
|
|
|
|
For agents available as VS Code extensions, add them to `.devcontainer/devcontainer.json`:
|
|
|
|
```jsonc
|
|
{
|
|
"customizations": {
|
|
"vscode": {
|
|
"extensions": [
|
|
// ... existing extensions ...
|
|
"[New Agent Extension ID]"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### CLI-based Agents
|
|
|
|
For agents that require CLI tools, add installation commands to `.devcontainer/post-create.sh`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
# Existing installations...
|
|
|
|
echo -e "\n🤖 Installing [New Agent Name] CLI..."
|
|
# run_command "npm install -g [agent-cli-package]@latest"
|
|
echo "✅ Done"
|
|
```
|
|
|
|
---
|
|
|
|
## Command File Formats
|
|
|
|
### Markdown Format
|
|
|
|
**Standard format:**
|
|
|
|
```markdown
|
|
---
|
|
description: "Command description"
|
|
---
|
|
|
|
Command content with {SCRIPT} and $ARGUMENTS placeholders.
|
|
```
|
|
|
|
**GitHub Copilot Chat Mode format:**
|
|
|
|
```markdown
|
|
---
|
|
description: "Command description"
|
|
mode: speckit.command-name
|
|
---
|
|
|
|
Command content with {SCRIPT} and $ARGUMENTS placeholders.
|
|
```
|
|
|
|
### TOML Format
|
|
|
|
```toml
|
|
description = "Command description"
|
|
|
|
prompt = """
|
|
Command content with {SCRIPT} and {{args}} placeholders.
|
|
"""
|
|
```
|
|
|
|
## Argument Patterns
|
|
|
|
Different agents use different argument placeholders. The placeholder used in command files is always taken from `registrar_config["args"]` for each integration — check there first when in doubt:
|
|
|
|
- **Markdown/prompt-based**: `$ARGUMENTS` (default for most markdown agents)
|
|
- **TOML-based**: `{{args}}` (e.g., Gemini)
|
|
- **Custom**: some agents override the default (e.g., Forge uses `{{parameters}}`)
|
|
- **Script placeholders**: `{SCRIPT}` (replaced with actual script path)
|
|
- **Agent placeholders**: `__AGENT__` (replaced with agent name)
|
|
|
|
## Common Pitfalls
|
|
|
|
1. **Using shorthand keys for CLI-based integrations**: For CLI-based integrations (`requires_cli: True`), the `key` must match the executable name (e.g., `"cursor-agent"` not `"cursor"`). `shutil.which(key)` is used for CLI tool checks — mismatches require special-case mappings. IDE-based integrations (`requires_cli: False`) are not subject to this constraint.
|
|
2. **Forgetting update scripts**: Both bash and PowerShell thin wrappers and the shared context-update scripts must be updated.
|
|
3. **Incorrect `requires_cli` value**: Set to `True` only for agents that have a CLI tool; set to `False` for IDE-based agents.
|
|
4. **Wrong argument format**: Use `$ARGUMENTS` for Markdown agents, `{{args}}` for TOML agents.
|
|
5. **Skipping registration**: The import and `_register()` call in `_register_builtins()` must both be added.
|
|
|
|
---
|
|
|
|
*This documentation should be updated whenever new integrations are added to maintain accuracy and completeness.*
|