Files
spec-kit/AGENTS.md
copilot-swe-agent[bot] 846c7f2afc 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>
2026-04-02 18:39:11 +00:00

12 KiB

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 package exposes a single class that declares all metadata, inherits setup/teardown logic from a base class, and registers itself in the global INTEGRATION_REGISTRY at import time.

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. Supported agents, their directories, formats, and capabilities are all derived from the integration classes — no separate tables or config dicts to maintain.


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/<key>/__init__.py. The key must match the actual CLI tool name (the executable users install and run). Use a Python-safe directory name if the key contains hyphens (e.g., kiro_cli/ for key "kiro-cli").

Minimal example — Markdown agent (Windsurf):

"""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):

"""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):

"""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; 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: 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.

3. Register it

In src/specify_cli/integrations/__init__.py, add one import and one _register() call inside _register_builtins(). Both lists are alphabetical:

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/<key>/scripts/ that delegate to the shared context-update scripts. Each is ~25 lines of boilerplate.

update-context.sh:

#!/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:

# 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, a switch case in Update-SpecificAgent, and an entry in Update-AllExistingAgents.

5. Test it

# Install into a test project
specify init my-project --integration <key>

# Verify files were created
ls -R my-project/<commands_dir>/

# Uninstall cleanly
cd my-project && specify integration uninstall <key>

Each integration also has a dedicated test file at tests/integrations/test_integration_<key>.py. Run it with:

pytest tests/integrations/test_integration_<key>.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:

{
  "customizations": {
    "vscode": {
      "extensions": [
        "// ... existing extensions ...",
        "// [New Agent Name]",
        "[New Agent Extension ID]"
      ]
    }
  }
}

CLI-based Agents

For agents that require CLI tools, add installation commands to .devcontainer/post-create.sh:

#!/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:

---
description: "Command description"
---

Command content with {SCRIPT} and $ARGUMENTS placeholders.

GitHub Copilot Chat Mode format:

---
description: "Command description"
mode: speckit.command-name
---

Command content with {SCRIPT} and $ARGUMENTS placeholders.

TOML Format

description = "Command description"

prompt = """
Command content with {SCRIPT} and {{args}} placeholders.
"""

Argument Patterns

Different agents use different argument placeholders:

  • Markdown/prompt-based: $ARGUMENTS
  • TOML-based: {{args}}
  • Script placeholders: {SCRIPT} (replaced with actual script path)
  • Agent placeholders: __AGENT__ (replaced with agent name)

Common Pitfalls

  1. Using shorthand keys instead of actual CLI tool names: The integration 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.
  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.