Full Code of github/spec-kit for AI

main d2559d7025e2 cached
130 files
1.3 MB
309.6k tokens
714 symbols
2 requests
Download .txt
Showing preview only (1,365K chars total). Download the full file or copy to clipboard to get everything.
Repository: github/spec-kit
Branch: main
Commit: d2559d7025e2
Files: 130
Total size: 1.3 MB

Directory structure:
gitextract_9kdterdf/

├── .devcontainer/
│   ├── devcontainer.json
│   └── post-create.sh
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── agent_request.yml
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── extension_submission.yml
│   │   ├── feature_request.yml
│   │   └── preset_submission.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── RELEASE-PROCESS.md
│       ├── codeql.yml
│       ├── docs.yml
│       ├── lint.yml
│       ├── release-trigger.yml
│       ├── release.yml
│       ├── scripts/
│       │   ├── check-release-exists.sh
│       │   ├── create-github-release.sh
│       │   ├── create-release-packages.ps1
│       │   ├── create-release-packages.sh
│       │   ├── generate-release-notes.sh
│       │   ├── get-next-version.sh
│       │   ├── simulate-release.sh
│       │   └── update-version.sh
│       ├── stale.yml
│       └── test.yml
├── .gitignore
├── .markdownlint-cli2.jsonc
├── AGENTS.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── docfx.json
│   ├── index.md
│   ├── installation.md
│   ├── local-development.md
│   ├── quickstart.md
│   ├── toc.yml
│   └── upgrade.md
├── extensions/
│   ├── EXTENSION-API-REFERENCE.md
│   ├── EXTENSION-DEVELOPMENT-GUIDE.md
│   ├── EXTENSION-PUBLISHING-GUIDE.md
│   ├── EXTENSION-USER-GUIDE.md
│   ├── README.md
│   ├── RFC-EXTENSION-SYSTEM.md
│   ├── catalog.community.json
│   ├── catalog.json
│   ├── selftest/
│   │   ├── commands/
│   │   │   └── selftest.md
│   │   └── extension.yml
│   └── template/
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── EXAMPLE-README.md
│       ├── LICENSE
│       ├── README.md
│       ├── commands/
│       │   └── example.md
│       ├── config-template.yml
│       └── extension.yml
├── newsletters/
│   └── 2026-February.md
├── presets/
│   ├── ARCHITECTURE.md
│   ├── PUBLISHING.md
│   ├── README.md
│   ├── catalog.community.json
│   ├── catalog.json
│   ├── scaffold/
│   │   ├── README.md
│   │   ├── commands/
│   │   │   ├── speckit.myext.myextcmd.md
│   │   │   └── speckit.specify.md
│   │   ├── preset.yml
│   │   └── templates/
│   │       ├── myext-template.md
│   │       └── spec-template.md
│   └── self-test/
│       ├── commands/
│       │   └── speckit.specify.md
│       ├── preset.yml
│       └── templates/
│           ├── agent-file-template.md
│           ├── checklist-template.md
│           ├── constitution-template.md
│           ├── plan-template.md
│           ├── spec-template.md
│           └── tasks-template.md
├── pyproject.toml
├── scripts/
│   ├── bash/
│   │   ├── check-prerequisites.sh
│   │   ├── common.sh
│   │   ├── create-new-feature.sh
│   │   ├── setup-plan.sh
│   │   └── update-agent-context.sh
│   └── powershell/
│       ├── check-prerequisites.ps1
│       ├── common.ps1
│       ├── create-new-feature.ps1
│       ├── setup-plan.ps1
│       └── update-agent-context.ps1
├── spec-driven.md
├── spec-kit.code-workspace
├── src/
│   └── specify_cli/
│       ├── __init__.py
│       ├── agents.py
│       ├── extensions.py
│       └── presets.py
├── templates/
│   ├── agent-file-template.md
│   ├── checklist-template.md
│   ├── commands/
│   │   ├── analyze.md
│   │   ├── checklist.md
│   │   ├── clarify.md
│   │   ├── constitution.md
│   │   ├── implement.md
│   │   ├── plan.md
│   │   ├── specify.md
│   │   ├── tasks.md
│   │   └── taskstoissues.md
│   ├── constitution-template.md
│   ├── plan-template.md
│   ├── spec-template.md
│   ├── tasks-template.md
│   └── vscode-settings.json
└── tests/
    ├── __init__.py
    ├── hooks/
    │   ├── .specify/
    │   │   └── extensions.yml
    │   ├── TESTING.md
    │   ├── plan.md
    │   ├── spec.md
    │   └── tasks.md
    ├── test_agent_config_consistency.py
    ├── test_ai_skills.py
    ├── test_cursor_frontmatter.py
    ├── test_extensions.py
    ├── test_merge.py
    └── test_presets.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
	"name": "SpecKitDevContainer",
	// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
	"image": "mcr.microsoft.com/devcontainers/python:3.13-trixie", // based on Debian "Trixie" (13)
	"features": {
		"ghcr.io/devcontainers/features/common-utils:2": {
			"installZsh": true,
			"installOhMyZsh": true,
			"installOhMyZshConfig": true,
			"upgradePackages": true,
			"username": "devcontainer",
			"userUid": "automatic",
			"userGid": "automatic"
		},
		"ghcr.io/devcontainers/features/dotnet:2": {
			"version": "lts"
		},
		"ghcr.io/devcontainers/features/git:1": {
			"ppa": true,
			"version": "latest"
		},
		"ghcr.io/devcontainers/features/node": {
			"version": "lts"
		}
	},

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
  "forwardPorts": [
	8080 // for Spec-Kit documentation site
  ],
  "containerUser": "devcontainer",
  "updateRemoteUserUID": true,
  "postCreateCommand": "chmod +x ./.devcontainer/post-create.sh && ./.devcontainer/post-create.sh",
  "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
  "customizations": {
    "vscode": {
      "extensions": [
		"mhutchie.git-graph",
		"eamodio.gitlens",
		"anweber.reveal-button",
		"chrisdias.promptboost",
		// Github Copilot
		"GitHub.copilot",
		"GitHub.copilot-chat",
		// Codex
		"openai.chatgpt",
		// Kilo Code
		"kilocode.Kilo-Code",
		// Roo Code
		"RooVeterinaryInc.roo-cline",
		// Claude Code
		"anthropic.claude-code"
	],
      "settings": {
		"debug.javascript.autoAttachFilter": "disabled", // fix running commands in integrated terminal

		// Specify settings for Github Copilot
		"git.autofetch": true,
		"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
		}
      }
    }
  }
}


================================================
FILE: .devcontainer/post-create.sh
================================================
#!/bin/bash

# Exit immediately on error, treat unset variables as an error, and fail if any command in a pipeline fails.
set -euo pipefail

# Function to run a command and show logs only on error
run_command() {
    local command_to_run="$*"
    local output
    local exit_code

    # Capture all output (stdout and stderr)
    output=$(eval "$command_to_run" 2>&1) || exit_code=$?
    exit_code=${exit_code:-0}

    if [ $exit_code -ne 0 ]; then
        echo -e "\033[0;31m[ERROR] Command failed (Exit Code $exit_code): $command_to_run\033[0m" >&2
        echo -e "\033[0;31m$output\033[0m" >&2

        exit $exit_code
    fi
}

# Installing CLI-based AI Agents

echo -e "\n🤖 Installing Copilot CLI..."
run_command "npm install -g @github/copilot@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Claude CLI..."
run_command "npm install -g @anthropic-ai/claude-code@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Codex CLI..."
run_command "npm install -g @openai/codex@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Gemini CLI..."
run_command "npm install -g @google/gemini-cli@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Augie CLI..."
run_command "npm install -g @augmentcode/auggie@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Qwen Code CLI..."
run_command "npm install -g @qwen-code/qwen-code@latest"
echo "✅ Done"

echo -e "\n🤖 Installing OpenCode CLI..."
run_command "npm install -g opencode-ai@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Junie CLI..."
run_command "npm install -g @jetbrains/junie-cli@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Pi Coding Agent..."
run_command "npm install -g @mariozechner/pi-coding-agent@latest"
echo "✅ Done"

echo -e "\n🤖 Installing Kiro CLI..."
# https://kiro.dev/docs/cli/
KIRO_INSTALLER_URL="https://kiro.dev/install.sh"
KIRO_INSTALLER_SHA256="7487a65cf310b7fb59b357c4b5e6e3f3259d383f4394ecedb39acf70f307cffb"
KIRO_INSTALLER_PATH="$(mktemp)"

cleanup_kiro_installer() {
  rm -f "$KIRO_INSTALLER_PATH"
}
trap cleanup_kiro_installer EXIT

run_command "curl -fsSL \"$KIRO_INSTALLER_URL\" -o \"$KIRO_INSTALLER_PATH\""
run_command "echo \"$KIRO_INSTALLER_SHA256  $KIRO_INSTALLER_PATH\" | sha256sum -c -"

run_command "bash \"$KIRO_INSTALLER_PATH\""

kiro_binary=""
if command -v kiro-cli >/dev/null 2>&1; then
  kiro_binary="kiro-cli"
elif command -v kiro >/dev/null 2>&1; then
  kiro_binary="kiro"
else
  echo -e "\033[0;31m[ERROR] Kiro CLI installation did not create 'kiro-cli' or 'kiro' in PATH.\033[0m" >&2
  exit 1
fi

run_command "$kiro_binary --help > /dev/null"
echo "✅ Done"

echo -e "\n🤖 Installing Kimi CLI..."
# https://code.kimi.com
run_command "pipx install kimi-cli"
echo "✅ Done"

echo -e "\n🤖 Installing CodeBuddy CLI..."
run_command "npm install -g @tencent-ai/codebuddy-code@latest"
echo "✅ Done"

# Installing UV (Python package manager)
echo -e "\n🐍 Installing UV - Python Package Manager..."
run_command "pipx install uv"
echo "✅ Done"

# Installing DocFx (for documentation site)
echo -e "\n📚 Installing DocFx..."
run_command "dotnet tool update -g docfx"
echo "✅ Done"

echo -e "\n🧹 Cleaning cache..."
run_command "sudo apt-get autoclean"
run_command "sudo apt-get clean"

echo "✅ Setup completed. Happy coding! 🚀"


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf


================================================
FILE: .github/CODEOWNERS
================================================
# Global code owner
* @mnriem



================================================
FILE: .github/ISSUE_TEMPLATE/agent_request.yml
================================================
name: Agent Request
description: Request support for a new AI agent/assistant in Spec Kit
title: "[Agent]: Add support for "
labels: ["agent-request", "enhancement", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for requesting a new agent! Before submitting, please check if the agent is already supported.
        
        **Currently supported agents**: Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy, Qoder CLI, Kiro CLI, Amp, SHAI, Tabnine CLI, Antigravity, IBM Bob, Mistral Vibe, Kimi Code, Trae, Pi Coding Agent, iFlow CLI

  - type: input
    id: agent-name
    attributes:
      label: Agent Name
      description: What is the name of the AI agent/assistant?
      placeholder: "e.g., SuperCoder AI"
    validations:
      required: true

  - type: input
    id: website
    attributes:
      label: Official Website
      description: Link to the agent's official website or documentation
      placeholder: "https://..."
    validations:
      required: true

  - type: dropdown
    id: agent-type
    attributes:
      label: Agent Type
      description: How is the agent accessed?
      options:
        - CLI tool (command-line interface)
        - IDE extension/plugin
        - Both CLI and IDE
        - Other
    validations:
      required: true

  - type: input
    id: cli-command
    attributes:
      label: CLI Command (if applicable)
      description: What command is used to invoke the agent from terminal?
      placeholder: "e.g., supercode, ai-assistant"

  - type: input
    id: install-method
    attributes:
      label: Installation Method
      description: How is the agent installed?
      placeholder: "e.g., npm install -g supercode, pip install supercode, IDE marketplace"
    validations:
      required: true

  - type: textarea
    id: command-structure
    attributes:
      label: Command/Workflow Structure
      description: How does the agent define custom commands or workflows?
      placeholder: |
        - Command file format (Markdown, YAML, TOML, etc.)
        - Directory location (e.g., .supercode/commands/)
        - Example command file structure
    validations:
      required: true

  - type: textarea
    id: argument-pattern
    attributes:
      label: Argument Passing Pattern
      description: How does the agent handle arguments in commands?
      placeholder: |
        e.g., Uses {{args}}, $ARGUMENTS, %ARGS%, or other placeholder format
        Example: "Run test suite with {{args}}"

  - type: dropdown
    id: popularity
    attributes:
      label: Popularity/Usage
      description: How widely is this agent used?
      options:
        - Widely used (thousands+ of users)
        - Growing adoption (hundreds of users)
        - New/emerging (less than 100 users)
        - Unknown
    validations:
      required: true

  - type: textarea
    id: documentation
    attributes:
      label: Documentation Links
      description: Links to relevant documentation for custom commands/workflows
      placeholder: |
        - Command documentation: https://...
        - API/CLI reference: https://...
        - Examples: https://...

  - type: textarea
    id: use-case
    attributes:
      label: Use Case
      description: Why do you want this agent supported in Spec Kit?
      placeholder: Explain your workflow and how this agent fits into your development process
    validations:
      required: true

  - type: textarea
    id: example-command
    attributes:
      label: Example Command File
      description: If possible, provide an example of a command file for this agent
      render: markdown
      placeholder: |
        ```toml
        description = "Example command"
        prompt = "Do something with {{args}}"
        ```

  - type: checkboxes
    id: contribution
    attributes:
      label: Contribution
      description: Are you willing to help implement support for this agent?
      options:
        - label: I can help test the integration
        - label: I can provide example command files
        - label: I can help with documentation
        - label: I can submit a pull request for the integration

  - type: textarea
    id: context
    attributes:
      label: Additional Context
      description: Any other relevant information about this agent
      placeholder: Screenshots, community links, comparison to existing agents, etc.


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Report a bug or unexpected behavior in Specify CLI or Spec Kit
title: "[Bug]: "
labels: ["bug", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to report a bug! Please fill out the sections below to help us diagnose and fix the issue.

  - type: textarea
    id: description
    attributes:
      label: Bug Description
      description: A clear and concise description of what the bug is.
      placeholder: What went wrong?
    validations:
      required: true

  - type: textarea
    id: reproduce
    attributes:
      label: Steps to Reproduce
      description: Steps to reproduce the behavior
      placeholder: |
        1. Run command '...'
        2. Execute script '...'
        3. See error
    validations:
      required: true

  - type: textarea
    id: expected
    attributes:
      label: Expected Behavior
      description: What did you expect to happen?
      placeholder: Describe the expected outcome
    validations:
      required: true

  - type: textarea
    id: actual
    attributes:
      label: Actual Behavior
      description: What actually happened?
      placeholder: Describe what happened instead
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: Specify CLI Version
      description: "Run `specify version` or `pip show spec-kit`"
      placeholder: "e.g., 1.3.0"
    validations:
      required: true

  - type: dropdown
    id: ai-agent
    attributes:
      label: AI Agent
      description: Which AI agent are you using?
      options:
        - Claude Code
        - Gemini CLI
        - GitHub Copilot
        - Cursor
        - Qwen Code
        - opencode
        - Codex CLI
        - Windsurf
        - Kilo Code
        - Auggie CLI
        - Roo Code
        - CodeBuddy
        - Qoder CLI
        - Kiro CLI
        - Amp
        - SHAI
        - IBM Bob
        - Antigravity
        - Not applicable
    validations:
      required: true

  - type: input
    id: os
    attributes:
      label: Operating System
      description: Your operating system and version
      placeholder: "e.g., macOS 14.2, Ubuntu 22.04, Windows 11"
    validations:
      required: true

  - type: input
    id: python
    attributes:
      label: Python Version
      description: "Run `python --version` or `python3 --version`"
      placeholder: "e.g., Python 3.11.5"
    validations:
      required: true

  - type: textarea
    id: logs
    attributes:
      label: Error Logs
      description: Please paste any relevant error messages or logs
      render: shell
      placeholder: Paste error output here

  - type: textarea
    id: context
    attributes:
      label: Additional Context
      description: Add any other context about the problem
      placeholder: Screenshots, related issues, workarounds attempted, etc.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: 💬 General Discussion
    url: https://github.com/github/spec-kit/discussions
    about: Ask questions, share ideas, or discuss Spec-Driven Development
  - name: 📖 Documentation
    url: https://github.com/github/spec-kit/blob/main/README.md
    about: Read the Spec Kit documentation and guides
  - name: 🛠️ Extension Development Guide
    url: https://github.com/github/spec-kit/blob/main/extensions/EXTENSION-DEVELOPMENT-GUIDE.md
    about: Learn how to develop and publish Spec Kit extensions
  - name: 🤝 Contributing Guide
    url: https://github.com/github/spec-kit/blob/main/CONTRIBUTING.md
    about: Learn how to contribute to Spec Kit
  - name: 🔒 Security Issues
    url: https://github.com/github/spec-kit/blob/main/SECURITY.md
    about: Report security vulnerabilities privately


================================================
FILE: .github/ISSUE_TEMPLATE/extension_submission.yml
================================================
name: Extension Submission
description: Submit your extension to the Spec Kit catalog
title: "[Extension]: Add "
labels: ["extension-submission", "enhancement", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for contributing an extension! This template helps you submit your extension to the community catalog.
        
        **Before submitting:**
        - Review the [Extension Publishing Guide](https://github.com/github/spec-kit/blob/main/extensions/EXTENSION-PUBLISHING-GUIDE.md)
        - Ensure your extension has a valid `extension.yml` manifest
        - Create a GitHub release with a version tag (e.g., v1.0.0)
        - Test installation: `specify extension add --from <your-release-url>`

  - type: input
    id: extension-id
    attributes:
      label: Extension ID
      description: Unique extension identifier (lowercase with hyphens only)
      placeholder: "e.g., jira-integration"
    validations:
      required: true

  - type: input
    id: extension-name
    attributes:
      label: Extension Name
      description: Human-readable extension name
      placeholder: "e.g., Jira Integration"
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: Version
      description: Semantic version number
      placeholder: "e.g., 1.0.0"
    validations:
      required: true

  - type: textarea
    id: description
    attributes:
      label: Description
      description: Brief description of what your extension does (under 200 characters)
      placeholder: Integrates Jira issue tracking with Spec Kit workflows for seamless task management
    validations:
      required: true

  - type: input
    id: author
    attributes:
      label: Author
      description: Your name or organization
      placeholder: "e.g., John Doe or Acme Corp"
    validations:
      required: true

  - type: input
    id: repository
    attributes:
      label: Repository URL
      description: GitHub repository URL for your extension
      placeholder: "https://github.com/your-org/spec-kit-your-extension"
    validations:
      required: true

  - type: input
    id: download-url
    attributes:
      label: Download URL
      description: URL to the GitHub release archive (e.g., v1.0.0.zip)
      placeholder: "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip"
    validations:
      required: true

  - type: input
    id: license
    attributes:
      label: License
      description: Open source license type
      placeholder: "e.g., MIT, Apache-2.0"
    validations:
      required: true

  - type: input
    id: homepage
    attributes:
      label: Homepage (optional)
      description: Link to extension homepage or documentation site
      placeholder: "https://..."

  - type: input
    id: documentation
    attributes:
      label: Documentation URL (optional)
      description: Link to detailed documentation
      placeholder: "https://github.com/your-org/spec-kit-your-extension/blob/main/docs/"

  - type: input
    id: changelog
    attributes:
      label: Changelog URL (optional)
      description: Link to changelog file
      placeholder: "https://github.com/your-org/spec-kit-your-extension/blob/main/CHANGELOG.md"

  - type: input
    id: speckit-version
    attributes:
      label: Required Spec Kit Version
      description: Minimum Spec Kit version required
      placeholder: "e.g., >=0.1.0"
    validations:
      required: true

  - type: textarea
    id: required-tools
    attributes:
      label: Required Tools (optional)
      description: List any external tools or dependencies required
      placeholder: |
        - jira-cli (>=1.0.0) - required
        - python (>=3.8) - optional
      render: markdown

  - type: input
    id: commands-count
    attributes:
      label: Number of Commands
      description: How many commands does your extension provide?
      placeholder: "e.g., 3"
    validations:
      required: true

  - type: input
    id: hooks-count
    attributes:
      label: Number of Hooks (optional)
      description: How many hooks does your extension provide?
      placeholder: "e.g., 0"

  - type: textarea
    id: tags
    attributes:
      label: Tags
      description: 2-5 relevant tags (lowercase, separated by commas)
      placeholder: "issue-tracking, jira, atlassian, automation"
    validations:
      required: true

  - type: textarea
    id: features
    attributes:
      label: Key Features
      description: List the main features and capabilities of your extension
      placeholder: |
        - Create Jira issues from specs
        - Sync task status with Jira
        - Link specs to existing issues
        - Generate Jira reports
    validations:
      required: true

  - type: checkboxes
    id: testing
    attributes:
      label: Testing Checklist
      description: Confirm that your extension has been tested
      options:
        - label: Extension installs successfully via download URL
          required: true
        - label: All commands execute without errors
          required: true
        - label: Documentation is complete and accurate
          required: true
        - label: No security vulnerabilities identified
          required: true
        - label: Tested on at least one real project
          required: true

  - type: checkboxes
    id: requirements
    attributes:
      label: Submission Requirements
      description: Verify your extension meets all requirements
      options:
        - label: Valid `extension.yml` manifest included
          required: true
        - label: README.md with installation and usage instructions
          required: true
        - label: LICENSE file included
          required: true
        - label: GitHub release created with version tag
          required: true
        - label: All command files exist and are properly formatted
          required: true
        - label: Extension ID follows naming conventions (lowercase-with-hyphens)
          required: true

  - type: textarea
    id: testing-details
    attributes:
      label: Testing Details
      description: Describe how you tested your extension
      placeholder: |
        **Tested on:**
        - macOS 14.0 with Spec Kit v0.1.0
        - Linux Ubuntu 22.04 with Spec Kit v0.1.0
        
        **Test project:** [Link or description]
        
        **Test scenarios:**
        1. Installed extension
        2. Configured settings
        3. Ran all commands
        4. Verified outputs
    validations:
      required: true

  - type: textarea
    id: example-usage
    attributes:
      label: Example Usage
      description: Provide a simple example of using your extension
      render: markdown
      placeholder: |
        ```bash
        # Install extension
        specify extension add --from https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
        
        # Use a command
        /speckit.your-extension.command-name arg1 arg2
        ```
    validations:
      required: true

  - type: textarea
    id: catalog-entry
    attributes:
      label: Proposed Catalog Entry
      description: Provide the JSON entry for catalog.json (helps reviewers)
      render: json
      placeholder: |
        {
          "your-extension": {
            "name": "Your Extension",
            "id": "your-extension",
            "description": "Brief description",
            "author": "Your Name",
            "version": "1.0.0",
            "download_url": "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip",
            "repository": "https://github.com/your-org/spec-kit-your-extension",
            "homepage": "https://github.com/your-org/spec-kit-your-extension",
            "license": "MIT",
            "requires": {
              "speckit_version": ">=0.1.0"
            },
            "provides": {
              "commands": 3
            },
            "tags": ["category", "tool"],
            "verified": false,
            "downloads": 0,
            "stars": 0,
            "created_at": "2026-02-20T00:00:00Z",
            "updated_at": "2026-02-20T00:00:00Z"
          }
        }
    validations:
      required: true

  - type: textarea
    id: additional-context
    attributes:
      label: Additional Context
      description: Any other information that would help reviewers
      placeholder: Screenshots, demo videos, links to related projects, etc.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest a new feature or enhancement for Specify CLI or Spec Kit
title: "[Feature]: "
labels: ["enhancement", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for suggesting a feature! Please provide details below to help us understand and evaluate your request.

  - type: textarea
    id: problem
    attributes:
      label: Problem Statement
      description: Is your feature request related to a problem? Please describe.
      placeholder: "I'm frustrated when..."
    validations:
      required: true

  - type: textarea
    id: solution
    attributes:
      label: Proposed Solution
      description: Describe the solution you'd like
      placeholder: What would you like to happen?
    validations:
      required: true

  - type: textarea
    id: alternatives
    attributes:
      label: Alternatives Considered
      description: Have you considered any alternative solutions or workarounds?
      placeholder: What other approaches might work?

  - type: dropdown
    id: component
    attributes:
      label: Component
      description: Which component does this feature relate to?
      options:
        - Specify CLI (initialization, commands)
        - Spec templates (BDD, Testing Strategy, etc.)
        - Agent integrations (command files, workflows)
        - Scripts (Bash/PowerShell utilities)
        - Documentation
        - CI/CD workflows
        - Other
    validations:
      required: true

  - type: dropdown
    id: ai-agent
    attributes:
      label: AI Agent (if applicable)
      description: Does this feature relate to a specific AI agent?
      options:
        - All agents
        - Claude Code
        - Gemini CLI
        - GitHub Copilot
        - Cursor
        - Qwen Code
        - opencode
        - Codex CLI
        - Windsurf
        - Kilo Code
        - Auggie CLI
        - Roo Code
        - CodeBuddy
        - Qoder CLI
        - Kiro CLI
        - Amp
        - SHAI
        - IBM Bob
        - Antigravity
        - Not applicable

  - type: textarea
    id: use-cases
    attributes:
      label: Use Cases
      description: Describe specific use cases where this feature would be valuable
      placeholder: |
        1. When working on large projects...
        2. During spec review...
        3. When integrating with CI/CD...

  - type: textarea
    id: acceptance
    attributes:
      label: Acceptance Criteria
      description: How would you know this feature is complete and working?
      placeholder: |
        - [ ] Feature does X
        - [ ] Documentation is updated
        - [ ] Works with all supported agents

  - type: textarea
    id: context
    attributes:
      label: Additional Context
      description: Add any other context, screenshots, or examples
      placeholder: Links to similar features, mockups, related discussions, etc.


================================================
FILE: .github/ISSUE_TEMPLATE/preset_submission.yml
================================================
name: Preset Submission
description: Submit your preset to the Spec Kit preset catalog
title: "[Preset]: Add "
labels: ["preset-submission", "enhancement", "needs-triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for contributing a preset! This template helps you submit your preset to the community catalog.
        
        **Before submitting:**
        - Review the [Preset Publishing Guide](https://github.com/github/spec-kit/blob/main/presets/PUBLISHING.md)
        - Ensure your preset has a valid `preset.yml` manifest
        - Create a GitHub release with a version tag (e.g., v1.0.0)
        - Test installation from the release archive: `specify preset add --from <download-url>`

  - type: input
    id: preset-id
    attributes:
      label: Preset ID
      description: Unique preset identifier (lowercase with hyphens only)
      placeholder: "e.g., healthcare-compliance"
    validations:
      required: true

  - type: input
    id: preset-name
    attributes:
      label: Preset Name
      description: Human-readable preset name
      placeholder: "e.g., Healthcare Compliance"
    validations:
      required: true

  - type: input
    id: version
    attributes:
      label: Version
      description: Semantic version number
      placeholder: "e.g., 1.0.0"
    validations:
      required: true

  - type: textarea
    id: description
    attributes:
      label: Description
      description: Brief description of what your preset does (under 200 characters)
      placeholder: Enforces HIPAA-compliant spec workflows with audit templates and compliance checklists
    validations:
      required: true

  - type: input
    id: author
    attributes:
      label: Author
      description: Your name or organization
      placeholder: "e.g., John Doe or Acme Corp"
    validations:
      required: true

  - type: input
    id: repository
    attributes:
      label: Repository URL
      description: GitHub repository URL for your preset
      placeholder: "https://github.com/your-org/spec-kit-your-preset"
    validations:
      required: true

  - type: input
    id: download-url
    attributes:
      label: Download URL
      description: URL to the GitHub release archive for your preset (e.g., https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip)
      placeholder: "https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip"
    validations:
      required: true

  - type: input
    id: license
    attributes:
      label: License
      description: Open source license type
      placeholder: "e.g., MIT, Apache-2.0"
    validations:
      required: true

  - type: input
    id: speckit-version
    attributes:
      label: Required Spec Kit Version
      description: Minimum Spec Kit version required
      placeholder: "e.g., >=0.3.0"
    validations:
      required: true

  - type: textarea
    id: templates-provided
    attributes:
      label: Templates Provided
      description: List the template overrides your preset provides
      placeholder: |
        - spec-template.md — adds compliance section
        - plan-template.md — includes audit checkpoints
        - checklist-template.md — HIPAA compliance checklist
    validations:
      required: true

  - type: textarea
    id: commands-provided
    attributes:
      label: Commands Provided (optional)
      description: List any command overrides your preset provides
      placeholder: |
        - speckit.specify.md — customized for compliance workflows

  - type: textarea
    id: tags
    attributes:
      label: Tags
      description: 2-5 relevant tags (lowercase, separated by commas)
      placeholder: "compliance, healthcare, hipaa, audit"
    validations:
      required: true

  - type: textarea
    id: features
    attributes:
      label: Key Features
      description: List the main features and capabilities of your preset
      placeholder: |
        - HIPAA-compliant spec templates
        - Audit trail checklists
        - Compliance review workflow
    validations:
      required: true

  - type: checkboxes
    id: testing
    attributes:
      label: Testing Checklist
      description: Confirm that your preset has been tested
      options:
        - label: Preset installs successfully via `specify preset add`
          required: true
        - label: Template resolution works correctly after installation
          required: true
        - label: Documentation is complete and accurate
          required: true
        - label: Tested on at least one real project
          required: true

  - type: checkboxes
    id: requirements
    attributes:
      label: Submission Requirements
      description: Verify your preset meets all requirements
      options:
        - label: Valid `preset.yml` manifest included
          required: true
        - label: README.md with description and usage instructions
          required: true
        - label: LICENSE file included
          required: true
        - label: GitHub release created with version tag
          required: true
        - label: Preset ID follows naming conventions (lowercase-with-hyphens)
          required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Description

<!-- What does this PR do? Why is it needed? -->

## Testing

<!-- How did you test your changes? -->

- [ ] Tested locally with `uv run specify --help`
- [ ] Ran existing tests with `uv sync && uv run pytest`
- [ ] Tested with a sample project (if applicable)

## AI Disclosure

<!-- Per our Contributing guidelines, AI assistance must be disclosed. -->
<!-- See: https://github.com/github/spec-kit/blob/main/CONTRIBUTING.md#ai-contributions-in-spec-kit -->

- [ ] I **did not** use AI assistance for this contribution
- [ ] I **did** use AI assistance (describe below)

<!-- If you used AI, briefly describe how (e.g., "Code generated by Copilot", "Consulted ChatGPT for approach"): -->



================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/RELEASE-PROCESS.md
================================================
# Release Process

This document describes the automated release process for Spec Kit.

## Overview

The release process is split into two workflows to ensure version consistency:

1. **Release Trigger Workflow** (`release-trigger.yml`) - Manages versioning and triggers release
2. **Release Workflow** (`release.yml`) - Builds and publishes artifacts

This separation ensures that git tags always point to commits with the correct version in `pyproject.toml`.

## Before Creating a Release

**Important**: Write clear, descriptive commit messages!

### How CHANGELOG.md Works

The CHANGELOG is **automatically generated** from your git commit messages:

1. **During Development**: Write clear, descriptive commit messages:
   ```bash
   git commit -m "feat: Add new authentication feature"
   git commit -m "fix: Resolve timeout issue in API client (#123)"
   git commit -m "docs: Update installation instructions"
   ```

2. **When Releasing**: The release trigger workflow automatically:
   - Finds all commits since the last release tag
   - Formats them as changelog entries
   - Inserts them into CHANGELOG.md
   - Commits the updated changelog before creating the new tag

### Commit Message Best Practices

Good commit messages make good changelogs:
- **Be descriptive**: "Add user authentication" not "Update files"
- **Reference issues/PRs**: Include `(#123)` for automated linking
- **Use conventional commits** (optional): `feat:`, `fix:`, `docs:`, `chore:`
- **Keep it concise**: One line is ideal, details go in commit body

**Example commits that become good changelog entries:**
```
fix: prepend YAML frontmatter to Cursor .mdc files (#1699)
feat: add generic agent support with customizable command directories (#1639)
docs: document dual-catalog system for extensions (#1689)
```

## Creating a Release

### Option 1: Auto-Increment (Recommended for patches)

1. Go to **Actions** → **Release Trigger**
2. Click **Run workflow**
3. Leave the version field **empty**
4. Click **Run workflow**

The workflow will:
- Auto-increment the patch version (e.g., `0.1.10` → `0.1.11`)
- Update `pyproject.toml`
- Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag
- Commit changes to a `chore/release-vX.Y.Z` branch
- Create and push the git tag from that branch
- Open a PR to merge the version bump into `main`
- Trigger the release workflow automatically via the tag push

### Option 2: Manual Version (For major/minor bumps)

1. Go to **Actions** → **Release Trigger**
2. Click **Run workflow**
3. Enter the desired version (e.g., `0.2.0` or `v0.2.0`)
4. Click **Run workflow**

The workflow will:
- Use your specified version
- Update `pyproject.toml`
- Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag
- Commit changes to a `chore/release-vX.Y.Z` branch
- Create and push the git tag from that branch
- Open a PR to merge the version bump into `main`
- Trigger the release workflow automatically via the tag push

## What Happens Next

Once the release trigger workflow completes:

1. A `chore/release-vX.Y.Z` branch is pushed with the version bump commit
2. The git tag is pushed, pointing to that commit
3. The **Release Workflow** is automatically triggered by the tag push
4. Release artifacts are built for all supported agents
5. A GitHub Release is created with all assets
6. A PR is opened to merge the version bump branch into `main`

> **Note**: Merge the auto-opened PR after the release is published to keep `main` in sync.

## Workflow Details

### Release Trigger Workflow

**File**: `.github/workflows/release-trigger.yml`

**Trigger**: Manual (`workflow_dispatch`)

**Permissions Required**: `contents: write`

**Steps**:
1. Checkout repository
2. Determine version (manual or auto-increment)
3. Check if tag already exists (prevents duplicates)
4. Create `chore/release-vX.Y.Z` branch
5. Update `pyproject.toml`
6. Update `CHANGELOG.md` from git commits
7. Commit changes
8. Push branch and tag
9. Open PR to merge version bump into `main`

### Release Workflow

**File**: `.github/workflows/release.yml`

**Trigger**: Tag push (`v*`)

**Permissions Required**: `contents: write`

**Steps**:
1. Checkout repository at tag
2. Extract version from tag name
3. Check if release already exists
4. Build release package variants (all agents × shell/powershell)
5. Generate release notes from commits
6. Create GitHub Release with all assets

## Version Constraints

- Tags must follow format: `v{MAJOR}.{MINOR}.{PATCH}`
- Example valid versions: `v0.1.11`, `v0.2.0`, `v1.0.0`
- Auto-increment only bumps patch version
- Cannot create duplicate tags (workflow will fail)

## Benefits of This Approach

✅ **Version Consistency**: Git tags point to commits with matching `pyproject.toml` version

✅ **Single Source of Truth**: Version set once, used everywhere

✅ **Prevents Drift**: No more manual version synchronization needed

✅ **Clean Separation**: Versioning logic separate from artifact building

✅ **Flexibility**: Supports both auto-increment and manual versioning

## Troubleshooting

### No Commits Since Last Release

If you run the release trigger workflow when there are no new commits since the last tag:
- The workflow will still succeed
- The CHANGELOG will show "- Initial release" if it's the first release
- Or it will be empty if there are no commits
- Consider adding meaningful commits before releasing

**Best Practice**: Use descriptive commit messages - they become your changelog!

### Tag Already Exists

If you see "Error: Tag vX.Y.Z already exists!", you need to:
- Choose a different version number, or
- Delete the existing tag if it was created in error

### Release Workflow Didn't Trigger

Check that:
- The release trigger workflow completed successfully
- The tag was pushed (check repository tags)
- The release workflow is enabled in Actions settings

### Version Mismatch

If `pyproject.toml` doesn't match the latest tag:
- Run the release trigger workflow to sync versions
- Or manually update `pyproject.toml` and push changes before running the release trigger

## Legacy Behavior (Pre-v0.1.10)

Before this change, the release workflow:
- Created tags automatically on main branch pushes
- Updated `pyproject.toml` AFTER creating the tag
- Resulted in tags pointing to commits with outdated versions

This has been fixed in v0.1.10+.


================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL"

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      contents: read
    strategy:
      fail-fast: false
      matrix:
        language: [ 'actions', 'python' ]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v4
        with:
          languages: ${{ matrix.language }}

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v4
        with:
          category: "/language:${{ matrix.language }}"


================================================
FILE: .github/workflows/docs.yml
================================================
# Build and deploy DocFX documentation to GitHub Pages
name: Deploy Documentation to Pages

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["main"]
    paths:
      - 'docs/**'

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: read
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0 # Fetch all history for git info

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.x'

      - name: Setup DocFX
        run: dotnet tool install -g docfx

      - name: Build with DocFX
        run: |
          cd docs
          docfx docfx.json

      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: 'docs/_site'

  # Deploy job
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4



================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
permissions:
  contents: read

on:
  push:
    branches: ["main"]
  pull_request:

jobs:
  markdownlint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Run markdownlint-cli2
        uses: DavidAnson/markdownlint-cli2-action@v19
        with:
          globs: |
            '**/*.md'
            !extensions/**/*.md


================================================
FILE: .github/workflows/release-trigger.yml
================================================
name: Release Trigger

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to release (e.g., 0.1.11). Leave empty to auto-increment patch version.'
        required: false
        type: string

jobs:
  bump-version:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
          token: ${{ secrets.RELEASE_PAT }}

      - name: Configure Git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Determine version
        id: version
        env:
          INPUT_VERSION: ${{ github.event.inputs.version }}
        run: |
          if [[ -n "$INPUT_VERSION" ]]; then
            # Manual version specified - strip optional v prefix
            VERSION="${INPUT_VERSION#v}"
            # Validate strict semver format to prevent injection
            if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
              echo "Error: Invalid version format '$VERSION'. Must be X.Y.Z (e.g. 1.2.3 or v1.2.3)"
              exit 1
            fi
            echo "version=$VERSION" >> $GITHUB_OUTPUT
            echo "tag=v$VERSION" >> $GITHUB_OUTPUT
            echo "Using manual version: $VERSION"
          else
            # Auto-increment patch version
            LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
            echo "Latest tag: $LATEST_TAG"

            # Extract version number and increment
            VERSION=$(echo $LATEST_TAG | sed 's/v//')
            IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
            MAJOR=${VERSION_PARTS[0]:-0}
            MINOR=${VERSION_PARTS[1]:-0}
            PATCH=${VERSION_PARTS[2]:-0}

            # Increment patch version
            PATCH=$((PATCH + 1))
            NEW_VERSION="$MAJOR.$MINOR.$PATCH"

            echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
            echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT
            echo "Auto-incremented version: $NEW_VERSION"
          fi

      - name: Check if tag already exists
        run: |
          if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
            echo "Error: Tag ${{ steps.version.outputs.tag }} already exists!"
            exit 1
          fi

      - name: Create release branch
        run: |
          BRANCH="chore/release-${{ steps.version.outputs.tag }}"
          git checkout -b "$BRANCH"
          echo "branch=$BRANCH" >> $GITHUB_ENV

      - name: Update pyproject.toml
        run: |
          sed -i "s/version = \".*\"/version = \"${{ steps.version.outputs.version }}\"/" pyproject.toml
          echo "Updated pyproject.toml to version ${{ steps.version.outputs.version }}"

      - name: Update CHANGELOG.md
        run: |
          if [ -f "CHANGELOG.md" ]; then
            DATE=$(date +%Y-%m-%d)

            # Get the previous tag by sorting all version tags numerically
            # (git describe --tags only finds tags reachable from HEAD,
            #  which misses tags on unmerged release branches)
            PREVIOUS_TAG=$(git tag -l 'v*' --sort=-version:refname | head -n 1)

            echo "Generating changelog from commits..."
            if [[ -n "$PREVIOUS_TAG" ]]; then
              echo "Changes since $PREVIOUS_TAG"
              COMMITS=$(git log --oneline "$PREVIOUS_TAG"..HEAD --no-merges --pretty=format:"- %s" 2>/dev/null || echo "- Initial release")
            else
              echo "No previous tag found - this is the first release"
              COMMITS="- Initial release"
            fi

            # Create new changelog entry
            {
              head -n 8 CHANGELOG.md
              echo ""
              echo "## [${{ steps.version.outputs.version }}] - $DATE"
              echo ""
              echo "### Changes"
              echo ""
              echo "$COMMITS"
              echo ""
              tail -n +9 CHANGELOG.md
            } > CHANGELOG.md.tmp
            mv CHANGELOG.md.tmp CHANGELOG.md

            echo "✅ Updated CHANGELOG.md with commits since $PREVIOUS_TAG"
          else
            echo "No CHANGELOG.md found"
          fi

      - name: Commit version bump
        run: |
          if [ -f "CHANGELOG.md" ]; then
            git add pyproject.toml CHANGELOG.md
          else
            git add pyproject.toml
          fi

          if git diff --cached --quiet; then
            echo "No changes to commit"
          else
            git commit -m "chore: bump version to ${{ steps.version.outputs.version }}"
            echo "Changes committed"
          fi

      - name: Create and push tag
        run: |
          git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
          git push origin "${{ env.branch }}"
          git push origin "${{ steps.version.outputs.tag }}"
          echo "Branch ${{ env.branch }} and tag ${{ steps.version.outputs.tag }} pushed"

      - name: Open pull request
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
        run: |
          gh pr create \
            --base main \
            --head "${{ env.branch }}" \
            --title "chore: bump version to ${{ steps.version.outputs.version }}" \
            --body "Automated version bump to ${{ steps.version.outputs.version }}.

          This PR was created by the Release Trigger workflow. The git tag \`${{ steps.version.outputs.tag }}\` has already been pushed and the release artifacts are being built.

          Merge this PR to record the version bump and changelog update on \`main\`."

      - name: Summary
        run: |
          echo "✅ Version bumped to ${{ steps.version.outputs.version }}"
          echo "✅ Tag ${{ steps.version.outputs.tag }} created and pushed"
          echo "✅ PR opened to merge version bump into main"
          echo "🚀 Release workflow is building artifacts from the tag"


================================================
FILE: .github/workflows/release.yml
================================================
name: Create Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract version from tag
        id: version
        run: |
          VERSION=${GITHUB_REF#refs/tags/}
          echo "tag=$VERSION" >> $GITHUB_OUTPUT
          echo "Building release for $VERSION"

      - name: Check if release already exists
        id: check_release
        run: |
          chmod +x .github/workflows/scripts/check-release-exists.sh
          .github/workflows/scripts/check-release-exists.sh ${{ steps.version.outputs.tag }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Create release package variants
        if: steps.check_release.outputs.exists == 'false'
        run: |
          chmod +x .github/workflows/scripts/create-release-packages.sh
          .github/workflows/scripts/create-release-packages.sh ${{ steps.version.outputs.tag }}

      - name: Generate release notes
        if: steps.check_release.outputs.exists == 'false'
        id: release_notes
        run: |
          chmod +x .github/workflows/scripts/generate-release-notes.sh
          # Get the previous tag for changelog generation
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${{ steps.version.outputs.tag }}^ 2>/dev/null || echo "")
          # Default to v0.0.0 if no previous tag is found (e.g., first release)
          if [ -z "$PREVIOUS_TAG" ]; then
            PREVIOUS_TAG="v0.0.0"
          fi
          .github/workflows/scripts/generate-release-notes.sh ${{ steps.version.outputs.tag }} "$PREVIOUS_TAG"

      - name: Create GitHub Release
        if: steps.check_release.outputs.exists == 'false'
        run: |
          chmod +x .github/workflows/scripts/create-github-release.sh
          .github/workflows/scripts/create-github-release.sh ${{ steps.version.outputs.tag }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}



================================================
FILE: .github/workflows/scripts/check-release-exists.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# check-release-exists.sh
# Check if a GitHub release already exists for the given version
# Usage: check-release-exists.sh <version>

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <version>" >&2
  exit 1
fi

VERSION="$1"

if gh release view "$VERSION" >/dev/null 2>&1; then
  echo "exists=true" >> $GITHUB_OUTPUT
  echo "Release $VERSION already exists, skipping..."
else
  echo "exists=false" >> $GITHUB_OUTPUT
  echo "Release $VERSION does not exist, proceeding..."
fi


================================================
FILE: .github/workflows/scripts/create-github-release.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# create-github-release.sh
# Create a GitHub release with all template zip files
# Usage: create-github-release.sh <version>

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <version>" >&2
  exit 1
fi

VERSION="$1"

# Remove 'v' prefix from version for release title
VERSION_NO_V=${VERSION#v}

gh release create "$VERSION" \
  .genreleases/spec-kit-template-copilot-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-copilot-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-claude-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-claude-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-gemini-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-gemini-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-cursor-agent-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-cursor-agent-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-opencode-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-opencode-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-qwen-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-qwen-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-windsurf-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-windsurf-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-junie-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-junie-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-codex-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-codex-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-kilocode-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-kilocode-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-auggie-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-auggie-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-roo-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-roo-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-qodercli-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-qodercli-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-amp-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-shai-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-shai-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-tabnine-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-tabnine-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-kiro-cli-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-kiro-cli-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-agy-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-agy-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-bob-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-bob-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-vibe-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-vibe-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-kimi-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-kimi-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-trae-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-trae-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-pi-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-pi-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-iflow-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-iflow-ps-"$VERSION".zip \
  .genreleases/spec-kit-template-generic-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-generic-ps-"$VERSION".zip \
  --title "Spec Kit Templates - $VERSION_NO_V" \
  --notes-file release_notes.md


================================================
FILE: .github/workflows/scripts/create-release-packages.ps1
================================================
#!/usr/bin/env pwsh
#requires -Version 7.0

<#
.SYNOPSIS
    Build Spec Kit template release archives for each supported AI assistant and script type.

.DESCRIPTION
    create-release-packages.ps1 (workflow-local)
    Build Spec Kit template release archives for each supported AI assistant and script type.

.PARAMETER Version
    Version string with leading 'v' (e.g., v0.2.0)

.PARAMETER Agents
    Comma or space separated subset of agents to build (default: all)
    Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, junie, codex, kilocode, auggie, roo, codebuddy, amp, kiro-cli, bob, qodercli, shai, tabnine, agy, vibe, kimi, trae, pi, iflow, generic

.PARAMETER Scripts
    Comma or space separated subset of script types to build (default: both)
    Valid scripts: sh, ps

.EXAMPLE
    .\create-release-packages.ps1 -Version v0.2.0

.EXAMPLE
    .\create-release-packages.ps1 -Version v0.2.0 -Agents claude,copilot -Scripts sh

.EXAMPLE
    .\create-release-packages.ps1 -Version v0.2.0 -Agents claude -Scripts ps
#>

param(
    [Parameter(Mandatory=$true, Position=0)]
    [string]$Version,

    [Parameter(Mandatory=$false)]
    [string]$Agents = "",

    [Parameter(Mandatory=$false)]
    [string]$Scripts = ""
)

$ErrorActionPreference = "Stop"

# Validate version format
if ($Version -notmatch '^v\d+\.\d+\.\d+$') {
    Write-Error "Version must look like v0.0.0"
    exit 1
}

Write-Host "Building release packages for $Version"

# Create and use .genreleases directory for all build artifacts
$GenReleasesDir = ".genreleases"
if (Test-Path $GenReleasesDir) {
    Remove-Item -Path $GenReleasesDir -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Path $GenReleasesDir -Force | Out-Null

function Rewrite-Paths {
    param([string]$Content)

    $Content = $Content -replace '(/?)\bmemory/', '.specify/memory/'
    $Content = $Content -replace '(/?)\bscripts/', '.specify/scripts/'
    $Content = $Content -replace '(/?)\btemplates/', '.specify/templates/'
    return $Content
}

function Generate-Commands {
    param(
        [string]$Agent,
        [string]$Extension,
        [string]$ArgFormat,
        [string]$OutputDir,
        [string]$ScriptVariant
    )

    New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null

    $templates = Get-ChildItem -Path "templates/commands/*.md" -File -ErrorAction SilentlyContinue

    foreach ($template in $templates) {
        $name = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)

        # Read file content and normalize line endings
        $fileContent = (Get-Content -Path $template.FullName -Raw) -replace "`r`n", "`n"

        # Extract description from YAML frontmatter
        $description = ""
        if ($fileContent -match '(?m)^description:\s*(.+)$') {
            $description = $matches[1]
        }

        # Extract script command from YAML frontmatter
        $scriptCommand = ""
        if ($fileContent -match "(?m)^\s*${ScriptVariant}:\s*(.+)$") {
            $scriptCommand = $matches[1]
        }

        if ([string]::IsNullOrEmpty($scriptCommand)) {
            Write-Warning "No script command found for $ScriptVariant in $($template.Name)"
            $scriptCommand = "(Missing script command for $ScriptVariant)"
        }

        # Extract agent_script command from YAML frontmatter if present
        $agentScriptCommand = ""
        if ($fileContent -match "(?ms)agent_scripts:.*?^\s*${ScriptVariant}:\s*(.+?)$") {
            $agentScriptCommand = $matches[1].Trim()
        }

        # Replace {SCRIPT} placeholder with the script command
        $body = $fileContent -replace '\{SCRIPT\}', $scriptCommand

        # Replace {AGENT_SCRIPT} placeholder with the agent script command if found
        if (-not [string]::IsNullOrEmpty($agentScriptCommand)) {
            $body = $body -replace '\{AGENT_SCRIPT\}', $agentScriptCommand
        }

        # Remove the scripts: and agent_scripts: sections from frontmatter
        $lines = $body -split "`n"
        $outputLines = @()
        $inFrontmatter = $false
        $skipScripts = $false
        $dashCount = 0

        foreach ($line in $lines) {
            if ($line -match '^---$') {
                $outputLines += $line
                $dashCount++
                if ($dashCount -eq 1) {
                    $inFrontmatter = $true
                } else {
                    $inFrontmatter = $false
                }
                continue
            }

            if ($inFrontmatter) {
                if ($line -match '^(scripts|agent_scripts):$') {
                    $skipScripts = $true
                    continue
                }
                if ($line -match '^[a-zA-Z].*:' -and $skipScripts) {
                    $skipScripts = $false
                }
                if ($skipScripts -and $line -match '^\s+') {
                    continue
                }
            }

            $outputLines += $line
        }

        $body = $outputLines -join "`n"

        # Apply other substitutions
        $body = $body -replace '\{ARGS\}', $ArgFormat
        $body = $body -replace '__AGENT__', $Agent
        $body = Rewrite-Paths -Content $body

        # Generate output file based on extension
        $outputFile = Join-Path $OutputDir "speckit.$name.$Extension"

        switch ($Extension) {
            'toml' {
                $body = $body -replace '\\', '\\'
                $output = "description = `"$description`"`n`nprompt = `"`"`"`n$body`n`"`"`""
                Set-Content -Path $outputFile -Value $output -NoNewline
            }
            'md' {
                Set-Content -Path $outputFile -Value $body -NoNewline
            }
            'agent.md' {
                Set-Content -Path $outputFile -Value $body -NoNewline
            }
        }
    }
}

function Generate-CopilotPrompts {
    param(
        [string]$AgentsDir,
        [string]$PromptsDir
    )

    New-Item -ItemType Directory -Path $PromptsDir -Force | Out-Null

    $agentFiles = Get-ChildItem -Path "$AgentsDir/speckit.*.agent.md" -File -ErrorAction SilentlyContinue

    foreach ($agentFile in $agentFiles) {
        $basename = $agentFile.Name -replace '\.agent\.md$', ''
        $promptFile = Join-Path $PromptsDir "$basename.prompt.md"

        $content = @"
---
agent: $basename
---
"@
        Set-Content -Path $promptFile -Value $content
    }
}

# Create skills in <skills_dir>\<name>\SKILL.md format.
# Most agents use hyphenated names (e.g. speckit-plan); Kimi is the
# current dotted-name exception (e.g. speckit.plan).
function New-Skills {
    param(
        [string]$SkillsDir,
        [string]$ScriptVariant,
        [string]$AgentName,
        [string]$Separator = '-'
    )

    $templates = Get-ChildItem -Path "templates/commands/*.md" -File -ErrorAction SilentlyContinue

    foreach ($template in $templates) {
        $name = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)
        $skillName = "speckit${Separator}$name"
        $skillDir = Join-Path $SkillsDir $skillName
        New-Item -ItemType Directory -Force -Path $skillDir | Out-Null

        $fileContent = (Get-Content -Path $template.FullName -Raw) -replace "`r`n", "`n"

        # Extract description
        $description = "Spec Kit: $name workflow"
        if ($fileContent -match '(?m)^description:\s*(.+)$') {
            $description = $matches[1]
        }

        # Extract script command
        $scriptCommand = "(Missing script command for $ScriptVariant)"
        if ($fileContent -match "(?m)^\s*${ScriptVariant}:\s*(.+)$") {
            $scriptCommand = $matches[1]
        }

        # Extract agent_script command from frontmatter if present
        $agentScriptCommand = ""
        if ($fileContent -match "(?ms)agent_scripts:.*?^\s*${ScriptVariant}:\s*(.+?)$") {
            $agentScriptCommand = $matches[1].Trim()
        }

        # Replace {SCRIPT}, strip scripts sections, rewrite paths
        $body = $fileContent -replace '\{SCRIPT\}', $scriptCommand
        if (-not [string]::IsNullOrEmpty($agentScriptCommand)) {
            $body = $body -replace '\{AGENT_SCRIPT\}', $agentScriptCommand
        }

        $lines = $body -split "`n"
        $outputLines = @()
        $inFrontmatter = $false
        $skipScripts = $false
        $dashCount = 0

        foreach ($line in $lines) {
            if ($line -match '^---$') {
                $outputLines += $line
                $dashCount++
                $inFrontmatter = ($dashCount -eq 1)
                continue
            }
            if ($inFrontmatter) {
                if ($line -match '^(scripts|agent_scripts):$') { $skipScripts = $true; continue }
                if ($line -match '^[a-zA-Z].*:' -and $skipScripts) { $skipScripts = $false }
                if ($skipScripts -and $line -match '^\s+') { continue }
            }
            $outputLines += $line
        }

        $body = $outputLines -join "`n"
        $body = $body -replace '\{ARGS\}', '$ARGUMENTS'
        $body = $body -replace '__AGENT__', $AgentName
        $body = Rewrite-Paths -Content $body

        # Strip existing frontmatter, keep only body
        $templateBody = ""
        $fmCount = 0
        $inBody = $false
        foreach ($line in ($body -split "`n")) {
            if ($line -match '^---$') {
                $fmCount++
                if ($fmCount -eq 2) { $inBody = $true }
                continue
            }
            if ($inBody) { $templateBody += "$line`n" }
        }

        $skillContent = "---`nname: `"$skillName`"`ndescription: `"$description`"`n---`n`n$templateBody"
        Set-Content -Path (Join-Path $skillDir "SKILL.md") -Value $skillContent -NoNewline
    }
}

function Build-Variant {
    param(
        [string]$Agent,
        [string]$Script
    )

    $baseDir = Join-Path $GenReleasesDir "sdd-${Agent}-package-${Script}"
    Write-Host "Building $Agent ($Script) package..."
    New-Item -ItemType Directory -Path $baseDir -Force | Out-Null

    # Copy base structure but filter scripts by variant
    $specDir = Join-Path $baseDir ".specify"
    New-Item -ItemType Directory -Path $specDir -Force | Out-Null

    # Copy memory directory
    if (Test-Path "memory") {
        Copy-Item -Path "memory" -Destination $specDir -Recurse -Force
        Write-Host "Copied memory -> .specify"
    }

    # Only copy the relevant script variant directory
    if (Test-Path "scripts") {
        $scriptsDestDir = Join-Path $specDir "scripts"
        New-Item -ItemType Directory -Path $scriptsDestDir -Force | Out-Null

        switch ($Script) {
            'sh' {
                if (Test-Path "scripts/bash") {
                    Copy-Item -Path "scripts/bash" -Destination $scriptsDestDir -Recurse -Force
                    Write-Host "Copied scripts/bash -> .specify/scripts"
                }
            }
            'ps' {
                if (Test-Path "scripts/powershell") {
                    Copy-Item -Path "scripts/powershell" -Destination $scriptsDestDir -Recurse -Force
                    Write-Host "Copied scripts/powershell -> .specify/scripts"
                }
            }
        }

        Get-ChildItem -Path "scripts" -File -ErrorAction SilentlyContinue | ForEach-Object {
            Copy-Item -Path $_.FullName -Destination $scriptsDestDir -Force
        }
    }

    # Copy templates (excluding commands directory and vscode-settings.json)
    if (Test-Path "templates") {
        $templatesDestDir = Join-Path $specDir "templates"
        New-Item -ItemType Directory -Path $templatesDestDir -Force | Out-Null

        Get-ChildItem -Path "templates" -Recurse -File | Where-Object {
            $_.FullName -notmatch 'templates[/\\]commands[/\\]' -and $_.Name -ne 'vscode-settings.json'
        } | ForEach-Object {
            $relativePath = $_.FullName.Substring((Resolve-Path "templates").Path.Length + 1)
            $destFile = Join-Path $templatesDestDir $relativePath
            $destFileDir = Split-Path $destFile -Parent
            New-Item -ItemType Directory -Path $destFileDir -Force | Out-Null
            Copy-Item -Path $_.FullName -Destination $destFile -Force
        }
        Write-Host "Copied templates -> .specify/templates"
    }

    # Generate agent-specific command files
    switch ($Agent) {
        'claude' {
            $cmdDir = Join-Path $baseDir ".claude/commands"
            Generate-Commands -Agent 'claude' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'gemini' {
            $cmdDir = Join-Path $baseDir ".gemini/commands"
            Generate-Commands -Agent 'gemini' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script
            if (Test-Path "agent_templates/gemini/GEMINI.md") {
                Copy-Item -Path "agent_templates/gemini/GEMINI.md" -Destination (Join-Path $baseDir "GEMINI.md")
            }
        }
        'copilot' {
            $agentsDir = Join-Path $baseDir ".github/agents"
            Generate-Commands -Agent 'copilot' -Extension 'agent.md' -ArgFormat '$ARGUMENTS' -OutputDir $agentsDir -ScriptVariant $Script

            $promptsDir = Join-Path $baseDir ".github/prompts"
            Generate-CopilotPrompts -AgentsDir $agentsDir -PromptsDir $promptsDir

            $vscodeDir = Join-Path $baseDir ".vscode"
            New-Item -ItemType Directory -Path $vscodeDir -Force | Out-Null
            if (Test-Path "templates/vscode-settings.json") {
                Copy-Item -Path "templates/vscode-settings.json" -Destination (Join-Path $vscodeDir "settings.json")
            }
        }
        'cursor-agent' {
            $cmdDir = Join-Path $baseDir ".cursor/commands"
            Generate-Commands -Agent 'cursor-agent' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'qwen' {
            $cmdDir = Join-Path $baseDir ".qwen/commands"
            Generate-Commands -Agent 'qwen' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
            if (Test-Path "agent_templates/qwen/QWEN.md") {
                Copy-Item -Path "agent_templates/qwen/QWEN.md" -Destination (Join-Path $baseDir "QWEN.md")
            }
        }
        'opencode' {
            $cmdDir = Join-Path $baseDir ".opencode/command"
            Generate-Commands -Agent 'opencode' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'windsurf' {
            $cmdDir = Join-Path $baseDir ".windsurf/workflows"
            Generate-Commands -Agent 'windsurf' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'junie' {
            $cmdDir = Join-Path $baseDir ".junie/commands"
            Generate-Commands -Agent 'junie' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'codex' {
            $skillsDir = Join-Path $baseDir ".agents/skills"
            New-Item -ItemType Directory -Force -Path $skillsDir | Out-Null
            New-Skills -SkillsDir $skillsDir -ScriptVariant $Script -AgentName 'codex' -Separator '-'
        }
        'kilocode' {
            $cmdDir = Join-Path $baseDir ".kilocode/workflows"
            Generate-Commands -Agent 'kilocode' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'auggie' {
            $cmdDir = Join-Path $baseDir ".augment/commands"
            Generate-Commands -Agent 'auggie' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'roo' {
            $cmdDir = Join-Path $baseDir ".roo/commands"
            Generate-Commands -Agent 'roo' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'codebuddy' {
            $cmdDir = Join-Path $baseDir ".codebuddy/commands"
            Generate-Commands -Agent 'codebuddy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'amp' {
            $cmdDir = Join-Path $baseDir ".agents/commands"
            Generate-Commands -Agent 'amp' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'kiro-cli' {
            $cmdDir = Join-Path $baseDir ".kiro/prompts"
            Generate-Commands -Agent 'kiro-cli' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'bob' {
            $cmdDir = Join-Path $baseDir ".bob/commands"
            Generate-Commands -Agent 'bob' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'qodercli' {
            $cmdDir = Join-Path $baseDir ".qoder/commands"
            Generate-Commands -Agent 'qodercli' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'shai' {
            $cmdDir = Join-Path $baseDir ".shai/commands"
            Generate-Commands -Agent 'shai' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'tabnine' {
            $cmdDir = Join-Path $baseDir ".tabnine/agent/commands"
            Generate-Commands -Agent 'tabnine' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script
            $tabnineTemplate = Join-Path 'agent_templates' 'tabnine/TABNINE.md'
            if (Test-Path $tabnineTemplate) { Copy-Item $tabnineTemplate (Join-Path $baseDir 'TABNINE.md') }
        }
        'agy' {
            $cmdDir = Join-Path $baseDir ".agent/commands"
            Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'vibe' {
            $cmdDir = Join-Path $baseDir ".vibe/prompts"
            Generate-Commands -Agent 'vibe' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'kimi' {
            $skillsDir = Join-Path $baseDir ".kimi/skills"
            New-Item -ItemType Directory -Force -Path $skillsDir | Out-Null
            New-Skills -SkillsDir $skillsDir -ScriptVariant $Script -AgentName 'kimi' -Separator '.'
        }
        'trae' {
            $rulesDir = Join-Path $baseDir ".trae/rules"
            New-Item -ItemType Directory -Force -Path $rulesDir | Out-Null
            Generate-Commands -Agent 'trae' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $rulesDir -ScriptVariant $Script
        }
        'pi' {
            $cmdDir = Join-Path $baseDir ".pi/prompts"
            Generate-Commands -Agent 'pi' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'iflow' {
            $cmdDir = Join-Path $baseDir ".iflow/commands"
            Generate-Commands -Agent 'iflow' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        'generic' {
            $cmdDir = Join-Path $baseDir ".speckit/commands"
            Generate-Commands -Agent 'generic' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
        }
        default {
            throw "Unsupported agent '$Agent'."
        }
    }

    # Create zip archive
    $zipFile = Join-Path $GenReleasesDir "spec-kit-template-${Agent}-${Script}-${Version}.zip"
    Compress-Archive -Path "$baseDir/*" -DestinationPath $zipFile -Force
    Write-Host "Created $zipFile"
}

# Define all agents and scripts
$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'junie', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'kiro-cli', 'bob', 'qodercli', 'shai', 'tabnine', 'agy', 'vibe', 'kimi', 'trae', 'pi', 'iflow', 'generic')
$AllScripts = @('sh', 'ps')

function Normalize-List {
    param([string]$Input)

    if ([string]::IsNullOrEmpty($Input)) {
        return @()
    }

    $items = $Input -split '[,\s]+' | Where-Object { $_ } | Select-Object -Unique
    return $items
}

function Validate-Subset {
    param(
        [string]$Type,
        [string[]]$Allowed,
        [string[]]$Items
    )

    $ok = $true
    foreach ($item in $Items) {
        if ($item -notin $Allowed) {
            Write-Error "Unknown $Type '$item' (allowed: $($Allowed -join ', '))"
            $ok = $false
        }
    }
    return $ok
}

# Determine agent list
if (-not [string]::IsNullOrEmpty($Agents)) {
    $AgentList = Normalize-List -Input $Agents
    if (-not (Validate-Subset -Type 'agent' -Allowed $AllAgents -Items $AgentList)) {
        exit 1
    }
} else {
    $AgentList = $AllAgents
}

# Determine script list
if (-not [string]::IsNullOrEmpty($Scripts)) {
    $ScriptList = Normalize-List -Input $Scripts
    if (-not (Validate-Subset -Type 'script' -Allowed $AllScripts -Items $ScriptList)) {
        exit 1
    }
} else {
    $ScriptList = $AllScripts
}

Write-Host "Agents: $($AgentList -join ', ')"
Write-Host "Scripts: $($ScriptList -join ', ')"

# Build all variants
foreach ($agent in $AgentList) {
    foreach ($script in $ScriptList) {
        Build-Variant -Agent $agent -Script $script
    }
}

Write-Host "`nArchives in ${GenReleasesDir}:"
Get-ChildItem -Path $GenReleasesDir -Filter "spec-kit-template-*-${Version}.zip" | ForEach-Object {
    Write-Host "  $($_.Name)"
}


================================================
FILE: .github/workflows/scripts/create-release-packages.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# create-release-packages.sh (workflow-local)
# Build Spec Kit template release archives for each supported AI assistant and script type.
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
#   Version argument should include leading 'v'.
#   Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
#     AGENTS  : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf junie codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli kimi trae pi iflow generic (default: all)
#     SCRIPTS : space or comma separated subset of: sh ps (default: both)
#   Examples:
#     AGENTS=claude SCRIPTS=sh $0 v0.2.0
#     AGENTS="copilot,gemini" $0 v0.2.0
#     SCRIPTS=ps $0 v0.2.0

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <version-with-v-prefix>" >&2
  exit 1
fi
NEW_VERSION="$1"
if [[ ! $NEW_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  echo "Version must look like v0.0.0" >&2
  exit 1
fi

echo "Building release packages for $NEW_VERSION"

# Create and use .genreleases directory for all build artifacts
GENRELEASES_DIR=".genreleases"
mkdir -p "$GENRELEASES_DIR"
rm -rf "$GENRELEASES_DIR"/* || true

rewrite_paths() {
  sed -E \
    -e 's@(/?)memory/@.specify/memory/@g' \
    -e 's@(/?)scripts/@.specify/scripts/@g' \
    -e 's@(/?)templates/@.specify/templates/@g' \
    -e 's@\.specify\.specify/@.specify/@g'
}

generate_commands() {
  local agent=$1 ext=$2 arg_format=$3 output_dir=$4 script_variant=$5
  mkdir -p "$output_dir"
  for template in templates/commands/*.md; do
    [[ -f "$template" ]] || continue
    local name description script_command agent_script_command body
    name=$(basename "$template" .md)

    # Normalize line endings
    file_content=$(tr -d '\r' < "$template")

    # Extract description and script command from YAML frontmatter
    description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
    script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')

    if [[ -z $script_command ]]; then
      echo "Warning: no script command found for $script_variant in $template" >&2
      script_command="(Missing script command for $script_variant)"
    fi

    # Extract agent_script command from YAML frontmatter if present
    agent_script_command=$(printf '%s\n' "$file_content" | awk '
      /^agent_scripts:$/ { in_agent_scripts=1; next }
      in_agent_scripts && /^[[:space:]]*'"$script_variant"':[[:space:]]*/ {
        sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, "")
        print
        exit
      }
      in_agent_scripts && /^[a-zA-Z]/ { in_agent_scripts=0 }
    ')

    # Replace {SCRIPT} placeholder with the script command
    body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")

    # Replace {AGENT_SCRIPT} placeholder with the agent script command if found
    if [[ -n $agent_script_command ]]; then
      body=$(printf '%s\n' "$body" | sed "s|{AGENT_SCRIPT}|${agent_script_command}|g")
    fi

    # Remove the scripts: and agent_scripts: sections from frontmatter while preserving YAML structure
    body=$(printf '%s\n' "$body" | awk '
      /^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
      in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
      in_frontmatter && /^agent_scripts:$/ { skip_scripts=1; next }
      in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
      in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
      { print }
    ')

    # Apply other substitutions
    body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)

    case $ext in
      toml)
        body=$(printf '%s\n' "$body" | sed 's/\\/\\\\/g')
        { echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
      md)
        echo "$body" > "$output_dir/speckit.$name.$ext" ;;
      agent.md)
        echo "$body" > "$output_dir/speckit.$name.$ext" ;;
    esac
  done
}

generate_copilot_prompts() {
  local agents_dir=$1 prompts_dir=$2
  mkdir -p "$prompts_dir"

  # Generate a .prompt.md file for each .agent.md file
  for agent_file in "$agents_dir"/speckit.*.agent.md; do
    [[ -f "$agent_file" ]] || continue

    local basename=$(basename "$agent_file" .agent.md)
    local prompt_file="$prompts_dir/${basename}.prompt.md"

    cat > "$prompt_file" <<EOF
---
agent: ${basename}
---
EOF
  done
}

# Create skills in <skills_dir>/<name>/SKILL.md format.
# Most agents use hyphenated names (e.g. speckit-plan); Kimi is the
# current dotted-name exception (e.g. speckit.plan).
create_skills() {
  local skills_dir="$1"
  local script_variant="$2"
  local agent_name="$3"
  local separator="${4:-"-"}"

  for template in templates/commands/*.md; do
    [[ -f "$template" ]] || continue
    local name
    name=$(basename "$template" .md)
    local skill_name="speckit${separator}${name}"
    local skill_dir="${skills_dir}/${skill_name}"
    mkdir -p "$skill_dir"

    local file_content
    file_content=$(tr -d '\r' < "$template")

    # Extract description from frontmatter
    local description
    description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
    [[ -z "$description" ]] && description="Spec Kit: ${name} workflow"

    # Extract script command
    local script_command
    script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')
    [[ -z "$script_command" ]] && script_command="(Missing script command for $script_variant)"

    # Extract agent_script command from frontmatter if present
    local agent_script_command
    agent_script_command=$(printf '%s\n' "$file_content" | awk '
      /^agent_scripts:$/ { in_agent_scripts=1; next }
      in_agent_scripts && /^[[:space:]]*'"$script_variant"':[[:space:]]*/ {
        sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, "")
        print
        exit
      }
      in_agent_scripts && /^[a-zA-Z]/ { in_agent_scripts=0 }
    ')

    # Build body: replace placeholders, strip scripts sections, rewrite paths
    local body
    body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
    if [[ -n $agent_script_command ]]; then
      body=$(printf '%s\n' "$body" | sed "s|{AGENT_SCRIPT}|${agent_script_command}|g")
    fi
    body=$(printf '%s\n' "$body" | awk '
      /^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
      in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
      in_frontmatter && /^agent_scripts:$/ { skip_scripts=1; next }
      in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
      in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
      { print }
    ')
    body=$(printf '%s\n' "$body" | sed 's/{ARGS}/\$ARGUMENTS/g' | sed "s/__AGENT__/$agent_name/g" | rewrite_paths)

    # Strip existing frontmatter and prepend skills frontmatter.
    local template_body
    template_body=$(printf '%s\n' "$body" | awk '/^---/{p++; if(p==2){found=1; next}} found')

    {
      printf -- '---\n'
      printf 'name: "%s"\n' "$skill_name"
      printf 'description: "%s"\n' "$description"
      printf -- '---\n\n'
      printf '%s\n' "$template_body"
    } > "$skill_dir/SKILL.md"
  done
}

build_variant() {
  local agent=$1 script=$2
  local base_dir="$GENRELEASES_DIR/sdd-${agent}-package-${script}"
  echo "Building $agent ($script) package..."
  mkdir -p "$base_dir"

  # Copy base structure but filter scripts by variant
  SPEC_DIR="$base_dir/.specify"
  mkdir -p "$SPEC_DIR"

  [[ -d memory ]] && { cp -r memory "$SPEC_DIR/"; echo "Copied memory -> .specify"; }

  # Only copy the relevant script variant directory
  if [[ -d scripts ]]; then
    mkdir -p "$SPEC_DIR/scripts"
    case $script in
      sh)
        [[ -d scripts/bash ]] && { cp -r scripts/bash "$SPEC_DIR/scripts/"; echo "Copied scripts/bash -> .specify/scripts"; }
        find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
        ;;
      ps)
        [[ -d scripts/powershell ]] && { cp -r scripts/powershell "$SPEC_DIR/scripts/"; echo "Copied scripts/powershell -> .specify/scripts"; }
        find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
        ;;
    esac
  fi

  [[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -not -name "vscode-settings.json" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }

  case $agent in
    claude)
      mkdir -p "$base_dir/.claude/commands"
      generate_commands claude md "\$ARGUMENTS" "$base_dir/.claude/commands" "$script" ;;
    gemini)
      mkdir -p "$base_dir/.gemini/commands"
      generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script"
      [[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;;
    copilot)
      mkdir -p "$base_dir/.github/agents"
      generate_commands copilot agent.md "\$ARGUMENTS" "$base_dir/.github/agents" "$script"
      generate_copilot_prompts "$base_dir/.github/agents" "$base_dir/.github/prompts"
      mkdir -p "$base_dir/.vscode"
      [[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json"
      ;;
    cursor-agent)
      mkdir -p "$base_dir/.cursor/commands"
      generate_commands cursor-agent md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
    qwen)
      mkdir -p "$base_dir/.qwen/commands"
      generate_commands qwen md "\$ARGUMENTS" "$base_dir/.qwen/commands" "$script"
      [[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md "$base_dir/QWEN.md" ;;
    opencode)
      mkdir -p "$base_dir/.opencode/command"
      generate_commands opencode md "\$ARGUMENTS" "$base_dir/.opencode/command" "$script" ;;
    windsurf)
      mkdir -p "$base_dir/.windsurf/workflows"
      generate_commands windsurf md "\$ARGUMENTS" "$base_dir/.windsurf/workflows" "$script" ;;
    junie)
      mkdir -p "$base_dir/.junie/commands"
      generate_commands junie md "\$ARGUMENTS" "$base_dir/.junie/commands" "$script" ;;
    codex)
      mkdir -p "$base_dir/.agents/skills"
      create_skills "$base_dir/.agents/skills" "$script" "codex" "-" ;;
    kilocode)
      mkdir -p "$base_dir/.kilocode/workflows"
      generate_commands kilocode md "\$ARGUMENTS" "$base_dir/.kilocode/workflows" "$script" ;;
    auggie)
      mkdir -p "$base_dir/.augment/commands"
      generate_commands auggie md "\$ARGUMENTS" "$base_dir/.augment/commands" "$script" ;;
    roo)
      mkdir -p "$base_dir/.roo/commands"
      generate_commands roo md "\$ARGUMENTS" "$base_dir/.roo/commands" "$script" ;;
    codebuddy)
      mkdir -p "$base_dir/.codebuddy/commands"
      generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;;
    qodercli)
      mkdir -p "$base_dir/.qoder/commands"
      generate_commands qodercli md "\$ARGUMENTS" "$base_dir/.qoder/commands" "$script" ;;
    amp)
      mkdir -p "$base_dir/.agents/commands"
      generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;;
    shai)
      mkdir -p "$base_dir/.shai/commands"
      generate_commands shai md "\$ARGUMENTS" "$base_dir/.shai/commands" "$script" ;;
    tabnine)
      mkdir -p "$base_dir/.tabnine/agent/commands"
      generate_commands tabnine toml "{{args}}" "$base_dir/.tabnine/agent/commands" "$script"
      [[ -f agent_templates/tabnine/TABNINE.md ]] && cp agent_templates/tabnine/TABNINE.md "$base_dir/TABNINE.md" ;;
    kiro-cli)
      mkdir -p "$base_dir/.kiro/prompts"
      generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;;
    agy)
      mkdir -p "$base_dir/.agent/commands"
      generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/commands" "$script" ;;
    bob)
      mkdir -p "$base_dir/.bob/commands"
      generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;
    vibe)
      mkdir -p "$base_dir/.vibe/prompts"
      generate_commands vibe md "\$ARGUMENTS" "$base_dir/.vibe/prompts" "$script" ;;
    kimi)
      mkdir -p "$base_dir/.kimi/skills"
      create_skills "$base_dir/.kimi/skills" "$script" "kimi" "." ;;
    trae)
      mkdir -p "$base_dir/.trae/rules"
      generate_commands trae md "\$ARGUMENTS" "$base_dir/.trae/rules" "$script" ;;
    pi)
      mkdir -p "$base_dir/.pi/prompts"
      generate_commands pi md "\$ARGUMENTS" "$base_dir/.pi/prompts" "$script" ;;
    iflow)
      mkdir -p "$base_dir/.iflow/commands"
      generate_commands iflow md "\$ARGUMENTS" "$base_dir/.iflow/commands" "$script" ;;
    generic)
      mkdir -p "$base_dir/.speckit/commands"
      generate_commands generic md "\$ARGUMENTS" "$base_dir/.speckit/commands" "$script" ;;
  esac
  ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
  echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
}

# Determine agent list
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf junie codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli kimi trae pi iflow generic)
ALL_SCRIPTS=(sh ps)

norm_list() {
  tr ',\n' '  ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?"\n":"") $i);out=1}}}END{printf("\n")}'
}

validate_subset() {
  local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
  local invalid=0
  for it in "${items[@]}"; do
    local found=0
    for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
    if [[ $found -eq 0 ]]; then
      echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
      invalid=1
    fi
  done
  return $invalid
}

if [[ -n ${AGENTS:-} ]]; then
  mapfile -t AGENT_LIST < <(printf '%s' "$AGENTS" | norm_list)
  validate_subset agent ALL_AGENTS "${AGENT_LIST[@]}" || exit 1
else
  AGENT_LIST=("${ALL_AGENTS[@]}")
fi

if [[ -n ${SCRIPTS:-} ]]; then
  mapfile -t SCRIPT_LIST < <(printf '%s' "$SCRIPTS" | norm_list)
  validate_subset script ALL_SCRIPTS "${SCRIPT_LIST[@]}" || exit 1
else
  SCRIPT_LIST=("${ALL_SCRIPTS[@]}")
fi

echo "Agents: ${AGENT_LIST[*]}"
echo "Scripts: ${SCRIPT_LIST[*]}"

for agent in "${AGENT_LIST[@]}"; do
  for script in "${SCRIPT_LIST[@]}"; do
    build_variant "$agent" "$script"
  done
done

echo "Archives in $GENRELEASES_DIR:"
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip


================================================
FILE: .github/workflows/scripts/generate-release-notes.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# generate-release-notes.sh
# Generate release notes from git history
# Usage: generate-release-notes.sh <new_version> <last_tag>

if [[ $# -ne 2 ]]; then
  echo "Usage: $0 <new_version> <last_tag>" >&2
  exit 1
fi

NEW_VERSION="$1"
LAST_TAG="$2"

# Get commits since last tag
if [ "$LAST_TAG" = "v0.0.0" ]; then
  # Check how many commits we have and use that as the limit
  COMMIT_COUNT=$(git rev-list --count HEAD)
  if [ "$COMMIT_COUNT" -gt 10 ]; then
    COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~10..HEAD)
  else
    COMMITS=$(git log --oneline --pretty=format:"- %s" HEAD~$COMMIT_COUNT..HEAD 2>/dev/null || git log --oneline --pretty=format:"- %s")
  fi
else
  COMMITS=$(git log --oneline --pretty=format:"- %s" $LAST_TAG..HEAD)
fi

# Create release notes
cat > release_notes.md << EOF
This is the latest set of releases that you can use with your agent of choice. We recommend using the Specify CLI to scaffold your projects, however you can download these independently and manage them yourself.

## Changelog

$COMMITS

EOF

echo "Generated release notes:"
cat release_notes.md


================================================
FILE: .github/workflows/scripts/get-next-version.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# get-next-version.sh
# Calculate the next version based on the latest git tag and output GitHub Actions variables
# Usage: get-next-version.sh

# Get the latest tag, or use v0.0.0 if no tags exist
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT

# Extract version number and increment
VERSION=$(echo $LATEST_TAG | sed 's/v//')
IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
MAJOR=${VERSION_PARTS[0]:-0}
MINOR=${VERSION_PARTS[1]:-0}
PATCH=${VERSION_PARTS[2]:-0}

# Increment patch version
PATCH=$((PATCH + 1))
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"

echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version will be: $NEW_VERSION"


================================================
FILE: .github/workflows/scripts/simulate-release.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# simulate-release.sh
# Simulate the release process locally without pushing to GitHub
# Usage: simulate-release.sh [version]
#   If version is omitted, auto-increments patch version

# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

echo -e "${BLUE}🧪 Simulating Release Process Locally${NC}"
echo "======================================"
echo ""

# Step 1: Determine version
if [[ -n "${1:-}" ]]; then
  VERSION="${1#v}"
  TAG="v$VERSION"
  echo -e "${GREEN}📝 Using manual version: $VERSION${NC}"
else
  LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
  echo -e "${BLUE}Latest tag: $LATEST_TAG${NC}"
  
  VERSION=$(echo $LATEST_TAG | sed 's/v//')
  IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
  MAJOR=${VERSION_PARTS[0]:-0}
  MINOR=${VERSION_PARTS[1]:-0}
  PATCH=${VERSION_PARTS[2]:-0}
  
  PATCH=$((PATCH + 1))
  VERSION="$MAJOR.$MINOR.$PATCH"
  TAG="v$VERSION"
  echo -e "${GREEN}📝 Auto-incremented to: $VERSION${NC}"
fi

echo ""

# Step 2: Check if tag exists
if git rev-parse "$TAG" >/dev/null 2>&1; then
  echo -e "${RED}❌ Error: Tag $TAG already exists!${NC}"
  echo "   Please use a different version or delete the tag first."
  exit 1
fi
echo -e "${GREEN}✓ Tag $TAG is available${NC}"

# Step 3: Backup current state
echo ""
echo -e "${YELLOW}💾 Creating backup of current state...${NC}"
BACKUP_DIR=$(mktemp -d)
cp pyproject.toml "$BACKUP_DIR/pyproject.toml.bak"
cp CHANGELOG.md "$BACKUP_DIR/CHANGELOG.md.bak"
echo -e "${GREEN}✓ Backup created at: $BACKUP_DIR${NC}"

# Step 4: Update pyproject.toml
echo ""
echo -e "${YELLOW}📝 Updating pyproject.toml...${NC}"
sed -i.tmp "s/version = \".*\"/version = \"$VERSION\"/" pyproject.toml
rm -f pyproject.toml.tmp
echo -e "${GREEN}✓ Updated pyproject.toml to version $VERSION${NC}"

# Step 5: Update CHANGELOG.md
echo ""
echo -e "${YELLOW}📝 Updating CHANGELOG.md...${NC}"
DATE=$(date +%Y-%m-%d)

# Get the previous tag to compare commits
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")

if [[ -n "$PREVIOUS_TAG" ]]; then
  echo "   Generating changelog from commits since $PREVIOUS_TAG"
  # Get commits since last tag, format as bullet points
  COMMITS=$(git log --oneline "$PREVIOUS_TAG"..HEAD --no-merges --pretty=format:"- %s" 2>/dev/null || echo "- Initial release")
else
  echo "   No previous tag found - this is the first release"
  COMMITS="- Initial release"
fi

# Create temp file with new entry
{
  head -n 8 CHANGELOG.md
  echo ""
  echo "## [$VERSION] - $DATE"
  echo ""
  echo "### Changed"
  echo ""
  echo "$COMMITS"
  echo ""
  tail -n +9 CHANGELOG.md
} > CHANGELOG.md.tmp
mv CHANGELOG.md.tmp CHANGELOG.md
echo -e "${GREEN}✓ Updated CHANGELOG.md with commits since $PREVIOUS_TAG${NC}"

# Step 6: Show what would be committed
echo ""
echo -e "${YELLOW}📋 Changes that would be committed:${NC}"
git diff pyproject.toml CHANGELOG.md

# Step 7: Create temporary tag (no push)
echo ""
echo -e "${YELLOW}🏷️  Creating temporary local tag...${NC}"
git tag -a "$TAG" -m "Simulated release $TAG" 2>/dev/null || true
echo -e "${GREEN}✓ Tag $TAG created locally${NC}"

# Step 8: Simulate release artifact creation
echo ""
echo -e "${YELLOW}📦 Simulating release package creation...${NC}"
echo "   (High-level simulation only; packaging script is not executed)"
echo ""

# Check if script exists and is executable
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [[ -x "$SCRIPT_DIR/create-release-packages.sh" ]]; then
  echo -e "${BLUE}In a real release, the following command would be run to create packages:${NC}"
  echo "  $SCRIPT_DIR/create-release-packages.sh \"$TAG\""
  echo ""
  echo "This simulation does not enumerate individual package files to avoid"
  echo "drifting from the actual behavior of create-release-packages.sh."
else
  echo -e "${RED}⚠️  create-release-packages.sh not found or not executable${NC}"
fi

# Step 9: Simulate release notes generation
echo ""
echo -e "${YELLOW}📄 Simulating release notes generation...${NC}"
echo ""
PREVIOUS_TAG=$(git describe --tags --abbrev=0 $TAG^ 2>/dev/null || echo "")
if [[ -n "$PREVIOUS_TAG" ]]; then
  echo -e "${BLUE}Changes since $PREVIOUS_TAG:${NC}"
  git log --oneline "$PREVIOUS_TAG".."$TAG" | head -n 10
  echo ""
else
  echo -e "${BLUE}No previous tag found - this would be the first release${NC}"
fi

# Step 10: Summary
echo ""
echo -e "${GREEN}🎉 Simulation Complete!${NC}"
echo "======================================"
echo ""
echo -e "${BLUE}Summary:${NC}"
echo "  Version: $VERSION"
echo "  Tag: $TAG"
echo "  Backup: $BACKUP_DIR"
echo ""
echo -e "${YELLOW}⚠️  SIMULATION ONLY - NO CHANGES PUSHED${NC}"
echo ""
echo -e "${BLUE}Next steps:${NC}"
echo "  1. Review the changes above"
echo "  2. To keep changes: git add pyproject.toml CHANGELOG.md && git commit"
echo "  3. To discard changes: git checkout pyproject.toml CHANGELOG.md && git tag -d $TAG"
echo "  4. To restore from backup: cp $BACKUP_DIR/* ."
echo ""
echo -e "${BLUE}To run the actual release:${NC}"
echo "  Go to: https://github.com/github/spec-kit/actions/workflows/release-trigger.yml"
echo "  Click 'Run workflow' and enter version: $VERSION"
echo ""


================================================
FILE: .github/workflows/scripts/update-version.sh
================================================
#!/usr/bin/env bash
set -euo pipefail

# update-version.sh
# Update version in pyproject.toml (for release artifacts only)
# Usage: update-version.sh <version>

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 <version>" >&2
  exit 1
fi

VERSION="$1"

# Remove 'v' prefix for Python versioning
PYTHON_VERSION=${VERSION#v}

if [ -f "pyproject.toml" ]; then
  sed -i "s/version = \".*\"/version = \"$PYTHON_VERSION\"/" pyproject.toml
  echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
else
  echo "Warning: pyproject.toml not found, skipping version update"
fi


================================================
FILE: .github/workflows/stale.yml
================================================
name: 'Close stale issues and PRs'

on:
  schedule:
    - cron: '0 0 * * *' # Run daily at midnight UTC
  workflow_dispatch: # Allow manual triggering

permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v10
        with:
          # Days of inactivity before an issue or PR becomes stale
          days-before-stale: 150
          # Days of inactivity before a stale issue or PR is closed (after being marked stale)
          days-before-close: 30
          
          # Stale issue settings
          stale-issue-message: 'This issue has been automatically marked as stale because it has not had any activity for 150 days. It will be closed in 30 days if no further activity occurs.'
          close-issue-message: 'This issue has been automatically closed due to inactivity (180 days total). If you believe this issue is still relevant, please reopen it or create a new issue.'
          stale-issue-label: 'stale'
          
          # Stale PR settings
          stale-pr-message: 'This pull request has been automatically marked as stale because it has not had any activity for 150 days. It will be closed in 30 days if no further activity occurs.'
          close-pr-message: 'This pull request has been automatically closed due to inactivity (180 days total). If you believe this PR is still relevant, please reopen it or create a new PR.'
          stale-pr-label: 'stale'
          
          # Exempt issues and PRs with these labels from being marked as stale
          exempt-issue-labels: 'pinned,security'
          exempt-pr-labels: 'pinned,security'
          
          # Only issues or PRs with all of these labels are checked
          # Leave empty to check all issues and PRs
          any-of-labels: ''
          
          # Operations per run (helps avoid rate limits)
          operations-per-run: 100


================================================
FILE: .github/workflows/test.yml
================================================
name: Test & Lint Python

permissions:
  contents: read

on:
  push:
    branches: ["main"]
  pull_request:

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v7

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.13"

      - name: Run ruff check
        run: uvx ruff check src/

  pytest:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install uv
        uses: astral-sh/setup-uv@v7

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --extra test

      - name: Run tests
        run: uv run pytest


================================================
FILE: .gitignore
================================================
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual environments
venv/
ENV/
env/
.venv

# IDE
.vscode/
.idea/
*.swp
*.swo
.DS_Store
*.tmp

# Project specific
*.log
.env
.env.local
*.lock

# Spec Kit-specific files
.genreleases/
*.zip
sdd-*/
docs/dev

# Extension system
.specify/extensions/.cache/
.specify/extensions/.backup/
.specify/extensions/*/local-config.yml


================================================
FILE: .markdownlint-cli2.jsonc
================================================
{
  // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
  "config": {
    "default": true,
    "MD003": {
      "style": "atx"
    },
    "MD007": {
      "indent": 2
    },
    "MD013": false,
    "MD024": {
      "siblings_only": true
    },
    "MD033": false,
    "MD041": false,
    "MD049": {
      "style": "asterisk"
    },
    "MD050": {
      "style": "asterisk"
    },
    "MD036": false,
    "MD060": false
  },
  "ignores": [
    ".genreleases/"
  ]
}

================================================
FILE: AGENTS.md
================================================
# 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.

---

## 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.

### Overview

Specify supports multiple AI agents by generating agent-specific command files and directory structures when initializing projects. Each agent has its own conventions for:

- **Command file formats** (Markdown, TOML, etc.)
- **Directory structures** (`.claude/commands/`, `.windsurf/workflows/`, etc.)
- **Command invocation patterns** (slash commands, CLI tools, etc.)
- **Argument passing conventions** (`$ARGUMENTS`, `{{args}}`, etc.)

### Current Supported Agents

| Agent                      | Directory              | Format   | CLI Tool        | Description                 |
| -------------------------- | ---------------------- | -------- | --------------- | --------------------------- |
| **Claude Code**            | `.claude/commands/`    | Markdown | `claude`        | Anthropic's Claude Code CLI |
| **Gemini CLI**             | `.gemini/commands/`    | TOML     | `gemini`        | Google's Gemini CLI         |
| **GitHub Copilot**         | `.github/agents/`      | Markdown | N/A (IDE-based) | GitHub Copilot in VS Code   |
| **Cursor**                 | `.cursor/commands/`    | Markdown | `cursor-agent`  | Cursor CLI                  |
| **Qwen Code**              | `.qwen/commands/`      | Markdown | `qwen`          | Alibaba's Qwen Code CLI     |
| **opencode**               | `.opencode/command/`   | Markdown | `opencode`      | opencode CLI                |
| **Codex CLI**              | `.codex/prompts/`      | Markdown | `codex`         | Codex CLI                   |
| **Windsurf**               | `.windsurf/workflows/` | Markdown | N/A (IDE-based) | Windsurf IDE workflows      |
| **Junie**                  | `.junie/commands/`     | Markdown | `junie`         | Junie by JetBrains          |
| **Kilo Code**              | `.kilocode/workflows/` | Markdown | N/A (IDE-based) | Kilo Code IDE               |
| **Auggie CLI**             | `.augment/commands/`   | Markdown | `auggie`        | Auggie CLI                  |
| **Roo Code**               | `.roo/commands/`       | Markdown | N/A (IDE-based) | Roo Code IDE                |
| **CodeBuddy CLI**          | `.codebuddy/commands/` | Markdown | `codebuddy`     | CodeBuddy CLI               |
| **Qoder CLI**              | `.qoder/commands/`     | Markdown | `qodercli`      | Qoder CLI                   |
| **Kiro CLI**               | `.kiro/prompts/`       | Markdown | `kiro-cli`      | Kiro CLI                    |
| **Amp**                    | `.agents/commands/`    | Markdown | `amp`           | Amp CLI                     |
| **SHAI**                   | `.shai/commands/`      | Markdown | `shai`          | SHAI CLI                    |
| **Tabnine CLI**            | `.tabnine/agent/commands/` | TOML | `tabnine`       | Tabnine CLI                 |
| **Kimi Code**              | `.kimi/skills/`        | Markdown | `kimi`          | Kimi Code CLI (Moonshot AI) |
| **Pi Coding Agent**        | `.pi/prompts/`         | Markdown | `pi`            | Pi terminal coding agent    |
| **iFlow CLI**              | `.iflow/commands/`     | Markdown | `iflow`         | iFlow CLI (iflow-ai)        |
| **IBM Bob**                | `.bob/commands/`       | Markdown | N/A (IDE-based) | IBM Bob IDE                 |
| **Trae**                   | `.trae/rules/`         | Markdown | N/A (IDE-based) | Trae IDE                    |
| **Generic**                | User-specified via `--ai-commands-dir` | Markdown | N/A | Bring your own agent        |

### Step-by-Step Integration Guide

Follow these steps to add a new agent (using a hypothetical new agent as an example):

#### 1. Add to AGENT_CONFIG

**IMPORTANT**: Use the actual CLI tool name as the key, not a shortened version.

Add the new agent to the `AGENT_CONFIG` dictionary in `src/specify_cli/__init__.py`. This is the **single source of truth** for all agent metadata:

```python
AGENT_CONFIG = {
    # ... existing agents ...
    "new-agent-cli": {  # Use the ACTUAL CLI tool name (what users type in terminal)
        "name": "New Agent Display Name",
        "folder": ".newagent/",  # Directory for agent files
        "commands_subdir": "commands",  # Subdirectory name for command files (default: "commands")
        "install_url": "https://example.com/install",  # URL for installation docs (or None if IDE-based)
        "requires_cli": True,  # True if CLI tool required, False for IDE-based agents
    },
}
```

**Key Design Principle**: The dictionary key should match the actual executable name that users install. For example:

- ✅ Use `"cursor-agent"` because the CLI tool is literally called `cursor-agent`
- ❌ Don't use `"cursor"` as a shortcut if the tool is `cursor-agent`

This eliminates the need for special-case mappings throughout the codebase.

**Field Explanations**:

- `name`: Human-readable display name shown to users
- `folder`: Directory where agent-specific files are stored (relative to project root)
- `commands_subdir`: Subdirectory name within the agent folder where command/prompt files are stored (default: `"commands"`)
  - Most agents use `"commands"` (e.g., `.claude/commands/`)
  - Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode), `"prompts"` (codex, kiro-cli, pi), `"command"` (opencode - singular)
  - This field enables `--ai-skills` to locate command templates correctly for skill generation
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
- `requires_cli`: Whether the agent requires a CLI tool check during initialization

#### 2. Update CLI Help Text

Update the `--ai` parameter help text in the `init()` command to include the new agent:

```python
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, new-agent-cli, or kiro-cli"),
```

Also update any function docstrings, examples, and error messages that list available agents.

#### 3. Update README Documentation

Update the **Supported AI Agents** section in `README.md` to include the new agent:

- Add the new agent to the table with appropriate support level (Full/Partial)
- Include the agent's official website link
- Add any relevant notes about the agent's implementation
- Ensure the table formatting remains aligned and consistent

#### 4. Update Release Package Script

Modify `.github/workflows/scripts/create-release-packages.sh`:

##### Add to ALL_AGENTS array

```bash
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf kiro-cli)
```

##### Add case statement for directory structure

```bash
case $agent in
  # ... existing cases ...
  windsurf)
    mkdir -p "$base_dir/.windsurf/workflows"
    generate_commands windsurf md "\$ARGUMENTS" "$base_dir/.windsurf/workflows" "$script" ;;
esac
```

#### 4. Update GitHub Release Script

Modify `.github/workflows/scripts/create-github-release.sh` to include the new agent's packages:

```bash
gh release create "$VERSION" \
  # ... existing packages ...
  .genreleases/spec-kit-template-windsurf-sh-"$VERSION".zip \
  .genreleases/spec-kit-template-windsurf-ps-"$VERSION".zip \
  # Add new agent packages here
```

#### 5. Update Agent Context Scripts

##### Bash script (`scripts/bash/update-agent-context.sh`)

Add file variable:

```bash
WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md"
```

Add to case statement:

```bash
case "$AGENT_TYPE" in
  # ... existing cases ...
  windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;;
  "")
    # ... existing checks ...
    [ -f "$WINDSURF_FILE" ] && update_agent_file "$WINDSURF_FILE" "Windsurf";
    # Update default creation condition
    ;;
esac
```

##### PowerShell script (`scripts/powershell/update-agent-context.ps1`)

Add file variable:

```powershell
$windsurfFile = Join-Path $repoRoot '.windsurf/rules/specify-rules.md'
```

Add to switch statement:

```powershell
switch ($AgentType) {
    # ... existing cases ...
    'windsurf' { Update-AgentFile $windsurfFile 'Windsurf' }
    '' {
        foreach ($pair in @(
            # ... existing pairs ...
            @{file=$windsurfFile; name='Windsurf'}
        )) {
            if (Test-Path $pair.file) { Update-AgentFile $pair.file $pair.name }
        }
        # Update default creation condition
    }
}
```

#### 6. Update CLI Tool Checks (Optional)

For agents that require CLI tools, add checks in the `check()` command and agent validation:

```python
# In check() command
tracker.add("windsurf", "Windsurf IDE (optional)")
windsurf_ok = check_tool_for_tracker("windsurf", "https://windsurf.com/", tracker)

# In init validation (only if CLI tool required)
elif selected_ai == "windsurf":
    if not check_tool("windsurf", "Install from: https://windsurf.com/"):
        console.print("[red]Error:[/red] Windsurf CLI is required for Windsurf projects")
        agent_tool_missing = True
```

**Note**: CLI tool checks are now handled automatically based on the `requires_cli` field in AGENT_CONFIG. No additional code changes needed in the `check()` or `init()` commands - they automatically loop through AGENT_CONFIG and check tools as needed.

## Important Design Decisions

### Using Actual CLI Tool Names as Keys

**CRITICAL**: When adding a new agent to AGENT_CONFIG, always use the **actual executable name** as the dictionary key, not a shortened or convenient version.

**Why this matters:**

- The `check_tool()` function uses `shutil.which(tool)` to find executables in the system PATH
- If the key doesn't match the actual CLI tool name, you'll need special-case mappings throughout the codebase
- This creates unnecessary complexity and maintenance burden

**Example - The Cursor Lesson:**

❌ **Wrong approach** (requires special-case mapping):

```python
AGENT_CONFIG = {
    "cursor": {  # Shorthand that doesn't match the actual tool
        "name": "Cursor",
        # ...
    }
}

# Then you need special cases everywhere:
cli_tool = agent_key
if agent_key == "cursor":
    cli_tool = "cursor-agent"  # Map to the real tool name
```

✅ **Correct approach** (no mapping needed):

```python
AGENT_CONFIG = {
    "cursor-agent": {  # Matches the actual executable name
        "name": "Cursor",
        # ...
    }
}

# No special cases needed - just use agent_key directly!
```

**Benefits of this approach:**

- Eliminates special-case logic scattered throughout the codebase
- Makes the code more maintainable and easier to understand
- Reduces the chance of bugs when adding new agents
- Tool checking "just works" without additional mappings

#### 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`:

```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`:

```bash
#!/bin/bash

# Existing installations...

echo -e "\n🤖 Installing [New Agent Name] CLI..."
# run_command "npm install -g [agent-cli-package]@latest" # Example for node-based CLI
# or other installation instructions (must be non-interactive and compatible with Linux Debian "Trixie" or later)...
echo "✅ Done"

```

**Quick Tips:**

- **Extension-based agents**: Add to the `extensions` array in `devcontainer.json`
- **CLI-based agents**: Add installation scripts to `post-create.sh`
- **Hybrid agents**: May require both extension and CLI installation
- **Test thoroughly**: Ensure installations work in the devcontainer environment

## Agent Categories

### CLI-Based Agents

Require a command-line tool to be installed:

- **Claude Code**: `claude` CLI
- **Gemini CLI**: `gemini` CLI
- **Cursor**: `cursor-agent` CLI
- **Qwen Code**: `qwen` CLI
- **opencode**: `opencode` CLI
- **Junie**: `junie` CLI
- **Kiro CLI**: `kiro-cli` CLI
- **CodeBuddy CLI**: `codebuddy` CLI
- **Qoder CLI**: `qodercli` CLI
- **Amp**: `amp` CLI
- **SHAI**: `shai` CLI
- **Tabnine CLI**: `tabnine` CLI
- **Kimi Code**: `kimi` CLI
- **Pi Coding Agent**: `pi` CLI

### IDE-Based Agents

Work within integrated development environments:

- **GitHub Copilot**: Built into VS Code/compatible editors
- **Windsurf**: Built into Windsurf IDE
- **IBM Bob**: Built into IBM Bob IDE

## Command File Formats

### Markdown Format

Used by: Claude, Cursor, opencode, Windsurf, Junie, Kiro CLI, Amp, SHAI, IBM Bob, Kimi Code, Qwen, Pi

**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

Used by: Gemini, Tabnine

```toml
description = "Command description"

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

## Directory Conventions

- **CLI agents**: Usually `.<agent-name>/commands/`
- **Common prompt-based exceptions**:
  - Codex: `.codex/prompts/`
  - Kiro CLI: `.kiro/prompts/`
  - Pi: `.pi/prompts/`
- **IDE agents**: Follow IDE-specific patterns:
  - Copilot: `.github/agents/`
  - Cursor: `.cursor/commands/`
  - Windsurf: `.windsurf/workflows/`

## 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)

## Testing New Agent Integration

1. **Build test**: Run package creation script locally
2. **CLI test**: Test `specify init --ai <agent>` command
3. **File generation**: Verify correct directory structure and files
4. **Command validation**: Ensure generated commands work with the agent
5. **Context update**: Test agent context update scripts

## Common Pitfalls

1. **Using shorthand keys instead of actual CLI tool names**: Always use the actual executable name as the AGENT_CONFIG key (e.g., `"cursor-agent"` not `"cursor"`). This prevents the need for special-case mappings throughout the codebase.
2. **Forgetting update scripts**: Both bash and PowerShell scripts must be updated when adding new agents.
3. **Incorrect `requires_cli` value**: Set to `True` only for agents that actually have CLI tools to check; set to `False` for IDE-based agents.
4. **Wrong argument format**: Use correct placeholder format for each agent type (`$ARGUMENTS` for Markdown, `{{args}}` for TOML).
5. **Directory naming**: Follow agent-specific conventions exactly (check existing agents for patterns).
6. **Help text inconsistency**: Update all user-facing text consistently (help strings, docstrings, README, error messages).

## Future Considerations

When adding new agents:

- Consider the agent's native command/workflow patterns
- Ensure compatibility with the Spec-Driven Development process
- Document any special requirements or limitations
- Update this guide with lessons learned
- Verify the actual CLI tool name before adding to AGENT_CONFIG

---

*This documentation should be updated whenever new agents are added to maintain accuracy and completeness.*


================================================
FILE: CHANGELOG.md
================================================
# Changelog

## [0.3.2] - 2026-03-19

### Changes

- Add conduct extension to community catalog (#1908)
- feat(extensions): add verify-tasks extension to community catalog (#1871)
- feat(presets): add enable/disable toggle and update semantics (#1891)
- feat: add iFlow CLI support (#1875)
- feat(commands): wire before/after hook events into specify and plan templates (#1886)
- docs(catalog): add speckit-utils to community catalog (#1896)
- docs: Add Extensions & Presets section to README (#1898)
- chore: update DocGuard extension to v0.9.11 (#1899)
- Update cognitive-squad catalog entry — Triadic Model, full lifecycle (#1884)
- feat: register spec-kit-iterate extension (#1887)
- fix(scripts): add explicit positional binding to PowerShell create-new-feature params (#1885)
- fix(scripts): encode residual JSON control chars as \uXXXX instead of stripping (#1872)
- chore: update DocGuard extension to v0.9.10 (#1890)
- Feature/spec kit add pi coding agent pullrequest (#1853)
- feat: register spec-kit-learn extension (#1883)


## [0.3.1] - 2026-03-17

### Changed

- docs: add greenfield Spring Boot pirate-speak preset demo to README (#1878)
- fix(ai-skills): exclude non-speckit copilot agent markdown from skills (#1867)
- feat: add Trae IDE support as a new agent (#1817)
- feat(cli): polite deep merge for settings.json and support JSONC (#1874)
- feat(extensions,presets): add priority-based resolution ordering (#1855)
- fix(scripts): suppress stdout from git fetch in create-new-feature.sh (#1876)
- fix(scripts): harden bash scripts — escape, compat, and error handling (#1869)
- Add cognitive-squad to community extension catalog (#1870)
- docs: add Go / React brownfield walkthrough to community walkthroughs (#1868)
- chore: update DocGuard extension to v0.9.8 (#1859)
- Feature: add specify status command (#1837)
- fix(extensions): show extension ID in list output (#1843)
- feat(extensions): add Archive and Reconcile extensions to community catalog (#1844)
- feat: Add DocGuard CDD enforcement extension to community catalog (#1838)


## [0.3.0] - 2026-03-13

### Changed

- No changes have been documented for this release yet.

<!-- Entries for 0.2.x and earlier releases are documented in their respective sections below. -->
- make c ignores consistent with c++ (#1747)
- chore: bump version to 0.1.13 (#1746)
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
- feat: add verify extension to community catalog (#1726)
- Add Retrospective Extension to community catalog README table (#1741)
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
- fix: correct Copilot extension command registration (#1724)
- fix(implement): remove Makefile from C ignore patterns (#1558)
- Add sync extension to community catalog (#1728)
- fix(checklist): clarify file handling behavior for append vs create (#1556)
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
- chore: bump version to 0.1.12 (#1737)
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
- 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(cli): polite deep merge for VSCode settings.json with JSONC support via `json5` and zero-data-loss fallbacks
- 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

- Added February 2026 newsletter (#1812)
- feat: add Kimi Code CLI agent support (#1790)
- docs: fix broken links in quickstart guide (#1759) (#1797)
- docs: add catalog cli help documentation (#1793) (#1794)
- fix: use quiet checkout to avoid exception on git checkout (#1792)
- feat(extensions): support .extensionignore to exclude files during install (#1781)
- feat: add Codex support for extension command registration (#1767)
- chore: bump version to 0.2.0 (#1786)
- fix: sync agent list comments with actual supported agents (#1785)
- feat(extensions): support multiple active catalogs simultaneously (#1720)
- Pavel/add tabnine cli support (#1503)
- Add Understanding extension to community catalog (#1778)
- Add ralph extension to community catalog (#1780)
- Update README with project initialization instructions (#1772)
- feat: add review extension to community catalog (#1775)
- Add fleet extension to community catalog (#1771)
- Integration of Mistral vibe support into speckit (#1725)
- fix: Remove duplicate options in specify.md (#1765)
- fix: use global branch numbering instead of per-short-name detection (#1757)
- Add Community Walkthroughs section to README (#1766)
- feat(extensions): add Jira Integration to community catalog (#1764)
- Add Azure DevOps Integration extension to community catalog (#1734)
- Fix docs: update Antigravity link and add initialization example (#1748)
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
- make c ignores consistent with c++ (#1747)
- chore: bump version to 0.1.13 (#1746)
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
- feat: add verify extension to community catalog (#1726)
- Add Retrospective Extension to community catalog README table (#1741)
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
- fix: correct Copilot extension command registration (#1724)
- fix(implement): remove Makefile from C ignore patterns (#1558)
- Add sync extension to community catalog (#1728)
- fix(checklist): clarify file handling behavior for append vs create (#1556)
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
- chore: bump version to 0.1.12 (#1737)
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
- 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)

## [0.2.0] - 2026-03-09

### Changed

- feat: add Kimi Code CLI agent support
- fix: sync agent list comments with actual supported agents (#1785)
- feat(extensions): support multiple active catalogs simultaneously (#1720)
- Pavel/add tabnine cli support (#1503)
- Add Understanding extension to community catalog (#1778)
- Add ralph extension to community catalog (#1780)
- Update README with project initialization instructions (#1772)
- feat: add review extension to community catalog (#1775)
- Add fleet extension to community catalog (#1771)
- Integration of Mistral vibe support into speckit (#1725)
- fix: Remove duplicate options in specify.md (#1765)
- fix: use global branch numbering instead of per-short-name detection (#1757)
- Add Community Walkthroughs section to README (#1766)
- feat(extensions): add Jira Integration to community catalog (#1764)
- Add Azure DevOps Integration extension to community catalog (#1734)
- Fix docs: update Antigravity link and add initialization example (#1748)
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
- make c ignores consistent with c++ (#1747)
- chore: bump version to 0.1.13 (#1746)
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
- feat: add verify extension to community catalog (#1726)
- Add Retrospective Extension to community catalog README table (#1741)
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
- fix: correct Copilot extension command registration (#1724)
- fix(implement): remove Makefile from C ignore patterns (#1558)
- Add sync extension to community catalog (#1728)
- fix(checklist): clarify file handling behavior for append vs create (#1556)
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
- chore: bump version to 0.1.12 (#1737)
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
- 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)

## [0.1.14] - 2026-03-09

### Added

- feat: add Tabnine CLI agent support
- **Multi-Catalog Support (#1707)**: Extension catalog system now supports multiple active catalogs simultaneously via a catalog stack
  - New `specify extension catalog list` command lists all active catalogs with name, URL, priority, and `install_allowed` status
  - New `specify extension catalog add` and `specify extension catalog remove` commands for project-scoped catalog management
  - Default built-in stack includes `catalog.json` (default, installable) and `catalog.community.json` (community, discovery only) — community extensions are now surfaced in search results out of the box
  - `specify extension search` aggregates results across all active catalogs, annotating each result with source catalog
  - `specify extension add` enforces `install_allowed` policy — extensions from discovery-only catalogs cannot be installed directly
  - Project-level `.specify/extension-catalogs.yml` and user-level `~/.specify/extension-catalogs.yml` config files supported, with project-level taking precedence
  - `SPECKIT_CATALOG_URL` environment variable still works for backward compatibility (replaces full stack with single catalog)
  - All catalog URLs require HTTPS (HTTP allowed for localhost development)
  - New `CatalogEntry` dataclass in `extensions.py` for catalog stack representation
  - Per-URL hash-based caching for non-default catalogs; legacy cache preserved for default catalog
  - Higher-priority catalogs win on merge conflicts (same extension id in multiple catalogs)
  - 13 new tests covering catalog stack resolution, merge conflicts, URL validation, and `install_allowed` enforcement
  - Updated RFC, Extension User Guide, and Extension API Reference documentation

## [0.1.13] - 2026-03-03

### Changed

- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
- feat: add verify extension to community catalog (#1726)
- Add Retrospective Extension to community catalog README table (#1741)
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
- fix: correct Copilot extension command registration (#1724)
- fix(implement): remove Makefile from C ignore patterns (#1558)
- Add sync extension to community catalog (#1728)
- fix(checklist): clarify file handling behavior for append vs create (#1556)
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
- chore: bump version to 0.1.12 (#1737)
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
- 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)


## [0.1.13] - 2026-03-03

### Fixed

- **Copilot Extension Commands Not Visible**: Fixed extension commands not appearing in GitHub Copilot when installed via `specify extension add --dev`
  - Changed Copilot file extension from `.md` to `.agent.md` in `CommandRegistrar.AGENT_CONFIGS` so Copilot recognizes agent files
  - Added generation of companion `.prompt.md` files in `.github/prompts/` during extension command registration, matching the release packaging behavior
  - Added cleanup of `.prompt.md` companion files when removing extensions via `specify extension remove`
- Fixed a syntax regression in `src/specify_cli/__init__.py` in `_build_ai_assistant_help()` that broke `ruff` and `pytest` collection in CI.
## [0.1.12] - 2026-03-02

### Changed

- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
- 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)


## [0.1.10] - 2026-03-02

### Fixed

- **Version Sync Issue (#1721)**: Fixed version mismatch between `pyproject.toml` and git release tags
  - Split release process into two workflows: `release-trigger.yml` for version management and `release.yml` for artifact building
  - Version bump now happens BEFORE tag creation, ensuring tags point to commits with correct version
  - Supports both manual version specification and auto-increment (patch version)
  - Git tags now accurately reflect the version in `pyproject.toml` at that commit
  - Prevents confusion when installing from source

## [0.1.9] - 2026-02-28

### Changed

- Updated dependency: bumped astral-sh/setup-uv from 6 to 7

## [0.1.8] - 2026-02-28

### Changed

- Updated dependency: bumped actions/setup-python from 5 to 6

## [0.1.7] - 2026-02-27

### Changed

- Updated outdated GitHub Actions versions
- Documented dual-catalog system for extensions

### Fixed

- Fixed version command in documentation

### Added

- Added Cleanup Extension to README
- Added retrospective extension to community catalog

## [0.1.6] - 2026-02-23

### Fixed

- **Parameter Ordering Issues (#1641)**: Fixed CLI parameter parsing issue where option flags were incorrectly consumed as values for preceding options
  - Added validation to detect when `--ai` or `--ai-commands-dir` incorrectly consume following flags like `--here` or `--ai-skills`
  - Now provides clear error messages: "Invalid value for --ai: '--here'"
  - Includes helpful hints suggesting proper usage and listing available agents
  - Commands like `specify init --ai-skills --ai --here` now fail with actionable feedback instead of confusing "Must specify project name" errors
  - Added comprehensive test suite (5 new tests) to prevent regressions

## [0.1.5] - 2026-02-21

### Fixed

- **AI Skills Installation Bug (#1658)**: Fixed `--ai-skills` flag not generating skill files for GitHub Copilot and other agents with non-standard command directory structures
  - Added `commands_subdir` field to `AGENT_CONFIG` to explicitly specify the subdirectory name for each agent
  - Affected agents now work correctly: copilot (`.github/agents/`), opencode (`.opencode/command/`), windsurf (`.windsurf/workflows/`), codex (`.codex/prompts/`), kilocode (`.kilocode/workflows/`), q (`.amazonq/prompts/`), and agy (`.agent/workflows/`)
  - The `install_ai_skills()` function now uses the correct path for all agents instead of assuming `commands/` for everyone

## [0.1.4] - 2026-02-20

### Fixed

- **Qoder CLI detection**: Renamed `AGENT_CONFIG` key from `"qoder"` to `"qodercli"` to match the actual executable name, fixing `specify check` and `specify init --ai` detection failures

## [0.1.3] - 2026-02-20

### Added

- **Generic Agent Support**: Added `--ai generic` option for unsupported AI agents ("bring your own agent")
  - Requires `--ai-commands-dir <path>` to specify where the agent reads commands from
  - Generates Markdown commands with `$ARGUMENTS` format (compatible with most agents)
  - Example: `specify init my-project --ai generic --ai-commands-dir .myagent/commands/`
  - Enables users to start with Spec Kit immediately while their agent awaits formal support

## [0.0.102] - 2026-02-20

- fix: include 'src/**' path in release workflow triggers (#1646)

## [0.0.101] - 2026-02-19

- chore(deps): bump github/codeql-action from 3 to 4 (#1635)

## [0.0.100] - 2026-02-19

- Add pytest and Python linting (ruff) to CI (#1637)
- feat: add pull request template for better contribution guidelines (#1634)

## [0.0.99] - 2026-02-19

- Feat/ai skills (#1632)

## [0.0.98] - 2026-02-19

- chore(deps): bump actions/stale from 9 to 10 (#1623)
- feat: add dependabot configuration for pip and GitHub Actions updates (#1622)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

- The use of sexualized language or imagery and unwelcome sexual attention or
  advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
  address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource@github.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Spec Kit

Hi there! We're thrilled that you'd like to contribute to Spec Kit. Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).

Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.

## Prerequisites for running and testing code

These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process.

1. Install [Python 3.11+](https://www.python.org/downloads/)
1. Install [uv](https://docs.astral.sh/uv/) for package management
1. Install [Git](https://git-scm.com/downloads)
1. Have an [AI coding agent available](README.md#-supported-ai-agents)

<details>
<summary><b>💡 Hint if you are using <code>VSCode</code> or <code>GitHub Codespaces</code> as your IDE</b></summary>

<br>

Provided you have [Docker](https://docker.com) installed on your machine, you can leverage [Dev Containers](https://containers.dev) through this [VSCode extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), to easily set up your development environment, with aforementioned tools already installed and configured, thanks to the `.devcontainer/devcontainer.json` file (located at the root of the project).

To do so, simply:

- Checkout the repo
- Open it with VSCode
- Open the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) and select "Dev Containers: Open Folder in Container..."

On [GitHub Codespaces](https://github.com/features/codespaces) it's even simpler, as it leverages the `.devcontainer/devcontainer.json` automatically upon opening the codespace.

</details>

## Submitting a pull request

> [!NOTE]
> If your pull request introduces a large change that materially impacts the work of the CLI or the rest of the repository (e.g., you're introducing new templates, arguments, or otherwise major changes), make sure that it was **discussed and agreed upon** by the project maintainers. Pull requests with large changes that did not have a prior conversation and agreement will be closed.

1. Fork and clone the repository
1. Configure and install the dependencies: `uv sync`
1. Make sure the CLI works on your machine: `uv run specify --help`
1. Create a new branch: `git checkout -b my-branch-name`
1. Make your change, add tests, and make sure everything still works
1. Test the CLI functionality with a sample project if relevant
1. Push to your fork and submit a pull request
1. Wait for your pull request to be reviewed and merged.

Here are a few things you can do that will increase the likelihood of your pull request being accepted:

- Follow the project's coding conventions.
- Write tests for new functionality.
- Update documentation (`README.md`, `spec-driven.md`) if your changes affect user-facing features.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
- Test your changes with the Spec-Driven Development workflow to ensure compatibility.

## Development workflow

When working on spec-kit:

1. Test changes with the `specify` CLI commands (`/speckit.specify`, `/speckit.plan`, `/speckit.tasks`) in your coding agent of choice
2. Verify templates are working correctly in `templates/` directory
3. Test script functionality in the `scripts/` directory
4. Ensure memory files (`memory/constitution.md`) are updated if major process changes are made

### Testing template and command changes locally

Running `uv run specify init` pulls released packages, which won’t include your local changes.  
To test your templates, commands, and other changes locally, follow these steps:

1. **Create release packages**

   Run the following command to generate the local packages:

   ```bash
   ./.github/workflows/scripts/create-release-packages.sh v1.0.0
   ```

2. **Copy the relevant package to your test project**

   ```bash
   cp -r .genreleases/sdd-copilot-package-sh/. <path-to-test-project>/
   ```

3. **Open and test the agent**

   Navigate to your test project folder and open the agent to verify your implementation.

## AI contributions in Spec Kit

> [!IMPORTANT]
>
> If you are using **any kind of AI assistance** to contribute to Spec Kit,
> it must be disclosed in the pull request or issue.

We welcome and encourage the use of AI tools to help improve Spec Kit! Many valuable contributions have been enhanced with AI assistance for code generation, issue detection, and feature definition.

That being said, if you are using any kind of AI assistance (e.g., agents, ChatGPT) while contributing to Spec Kit,
**this must be disclosed in the pull request or issue**, along with the extent to which AI assistance was used (e.g., documentation comments vs. code generation).

If your PR responses or comments are being generated by an AI, disclose that as well.

As an exception, trivial spacing or typo fixes don't need to be disclosed, so long as the changes are limited to small parts of the code or short phrases.

An example disclosure:

> This PR was written primarily by GitHub Copilot.

Or a more detailed disclosure:

> I consulted ChatGPT to understand the codebase but the solution
> was fully authored manually by myself.

Failure to disclose this is first and foremost rude to the human operators on the other end of the pull request, but it also makes it difficult to
determine how much scrutiny to apply to the contribution.

In a perfect world, AI assistance would produce equal or higher quality work than any human. That isn't the world we live in today, and in most cases
where human supervision or expertise is not in the loop, it's generating code that cannot be reasonably maintained or evolved.

### What we're looking for

When submitting AI-assisted contributions, please ensure they include:

- **Clear disclosure of AI use** - You are transparent about AI use and degree to which you're using it for the contribution
- **Human understanding and testing** - You've personally tested the changes and understand what they do
- **Clear rationale** - You can explain why the change is needed and how it fits within Spec Kit's goals
- **Concrete evidence** - Include test cases, scenarios, or examples that demonstrate the improvement
- **Your own analysis** - Share your thoughts on the end-to-end developer experience

### What we'll close

We reserve the right to close contributions that appear to be:

- Untested changes submitted without verification
- Generic suggestions that don't address specific Spec Kit needs
- Bulk submissions that show no human review or understanding

### Guidelines for success

The key is demonstrating that you understand and have validated your proposed changes. If a maintainer can easily tell that a contribution was generated entirely by AI without human input or testing, it likely needs more work before submission.

Contributors who consistently submit low-effort AI-generated changes may be restricted from further contributions at the maintainers' discretion.

Please be respectful to maintainers and disclose AI assistance.

## Resources

- [Spec-Driven Development Methodology](./spec-driven.md)
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)


================================================
FILE: LICENSE
================================================
MIT License

Copyright GitHub, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
<div align="center">
    <img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
    <h1>🌱 Spec Kit</h1>
    <h3><em>Build high-quality software faster.</em></h3>
</div>

<p align="center">
    <strong>An open source toolkit that allows you to focus on product scenarios and predictable outcomes instead of vibe coding every piece from scratch.</strong>
</p>

<p align="center">
    <a href="https://github.com/github/spec-kit/actions/workflows/release.yml"><img src="https://github.com/github/spec-kit/actions/workflows/release.yml/badge.svg" alt="Release"/></a>
    <a href="https://github.com/github/spec-kit/stargazers"><img src="https://img.shields.io/github/stars/github/spec-kit?style=social" alt="GitHub stars"/></a>
    <a href="https://github.com/github/spec-kit/blob/main/LICENSE"><img src="https://img.shields.io/github/license/github/spec-kit" alt="License"/></a>
    <a href="https://github.github.io/spec-kit/"><img src="https://img.shields.io/badge/docs-GitHub_Pages-blue" alt="Documentation"/></a>
</p>

---

## Table of Contents

- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
- [⚡ Get Started](#-get-started)
- [📽️ Video Overview](#️-video-overview)
- [🚶 Community Walkthroughs](#-community-walkthroughs)
- [🤖 Supported AI Agents](#-supported-ai-agents)
- [🔧 Specify CLI Reference](#-specify-cli-reference)
- [🧩 Making Spec Kit Your Own: Extensions & Presets](#-making-spec-kit-your-own-extensions--presets)
- [📚 Core Philosophy](#-core-philosophy)
- [🌟 Development Phases](#-development-phases)
- [🎯 Experimental Goals](#-experimental-goals)
- [🔧 Prerequisites](#-prerequisites)
- [📖 Learn More](#-learn-more)
- [📋 Detailed Process](#-detailed-process)
- [🔍 Troubleshooting](#-troubleshooting)
- [💬 Support](#-support)
- [🙏 Acknowledgements](#-acknowledgements)
- [📄 License](#-license)

## 🤔 What is Spec-Driven Development?

Spec-Driven Development **flips the script** on traditional software development. For decades, code has been king — specifications were just scaffolding we built and discarded once the "real work" of coding began. Spec-Driven Development changes this: **specifications become executable**, directly generating working implementations rather than just guiding them.

## ⚡ Get Started

### 1. Install Specify CLI

Choose your preferred installation method:

#### Option 1: Persistent Installation (Recommended)

Install once and use everywhere:

```bash
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
```

Then use the tool directly:

```bash
# Create new project
specify init <PROJECT_NAME>

# Or initialize in existing project
specify init . --ai claude
# or
specify init --here --ai claude

# Check installed tools
specify check
```

To upgrade Specify, see the [Upgrade Guide](./docs/upgrade.md) for detailed instructions. Quick upgrade:

```bash
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
```

#### Option 2: One-time Usage

Run directly without installing:

```bash
# Create new project
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>

# Or initialize in existing project
uvx --from git+https://github.com/github/spec-kit.git specify init . --ai claude
# or
uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai claude
```

**Benefits of persistent installation:**

- Tool stays installed and available in PATH
- No need to create shell aliases
- Better tool management with `uv tool list`, `uv tool upgrade`, `uv tool uninstall`
- Cleaner shell configuration

### 2. Establish project principles

Launch your AI assistant in the project directory. Most agents expose spec-kit as `/speckit.*` slash commands; Codex CLI in skills mode uses `$speckit-*` instead.

Use the **`/speckit.constitution`** command to create your project's governing principles and development guidelines that will guide all subsequent development.

```bash
/speckit.constitution Create principles focused on code quality, testing standards, user experience consistency, and performance requirements
```

### 3. Create the spec

Use the **`/speckit.specify`** command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.

```bash
/speckit.specify Build an application that can help me organize my photos in separate photo albums. Albums are grouped by date and can be re-organized by dragging and dropping on the main page. Albums are never in other nested albums. Within each album, photos are previewed in a tile-like interface.
```

### 4. Create a technical implementation plan

Use the **`/speckit.plan`** command to provide your tech stack and architecture choices.

```bash
/speckit.plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database.
```

### 5. Break down into tasks

Use **`/speckit.tasks`** to create an actionable task list from your implementation plan.

```bash
/speckit.tasks
```

### 6. Execute implementation

Use **`/speckit.implement`** to execute all tasks and build your feature according to the plan.

```bash
/speckit.implement
```

For detailed step-by-step instructions, see our [comprehensive guide](./spec-driven.md).

## 📽️ Video Overview

Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)!

[![Spec Kit video header](/media/spec-kit-video-header.jpg)](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)

## 🚶 Community Walkthroughs

See Spec-Driven Development in action across different scenarios with these community-contributed walkthroughs:

- **[Greenfield .NET CLI tool](https://github.com/mnriem/spec-kit-dotnet-cli-demo)** — Builds a Timezone Utility as a .NET single-binary CLI tool from a blank directory, covering the full spec-kit workflow: constitution, specify, plan, tasks, and multi-pass implement using GitHub Copilot agents.

- **[Greenfield Spring Boot + React platform](https://github.com/mnriem/spec-kit-spring-react-demo)** — Builds an LLM performance analytics platform (REST API, graphs, iteration tracking) from scratch using Spring Boot, embedded React, PostgreSQL, and Docker Compose, with a clarify step and a cross-artifact consistency analysis pass included.

- **[Brownfield ASP.NET CMS extension](https://github.com/mnriem/spec-kit-aspnet-brownfield-demo)** — Extends an existing open-source .NET CMS (CarrotCakeCMS-Core, ~307,000 lines of C#, Razor, SQL, JavaScript, and config files) with two new features — cross-platform Docker Compose infrastructure and a token-authenticated headless REST API — demonstrating how spec-kit fits into existing codebases without prior specs or a constitution.

- **[Brownfield Java runtime extension](https://github.com/mnriem/spec-kit-java-brownfield-demo)** — Extends an existing open-source Jakarta EE runtime (Piranha, ~420,000 lines of Java, XML, JSP, HTML, and config files across 180 Maven modules) with a password-protected Server Admin Console, demonstrating spec-kit on a large multi-module Java project with no prior specs or constitution.

- **[Brownfield Go / React dashboard demo](https://github.com/mnriem/spec-kit-go-brownfield-demo)** — Demonstrates spec-kit driven entirely from the **terminal using GitHub Copilot CLI**. Extends NASA's open-source Hermes ground support system (Go) with a lightweight React-based web telemetry dashboard, showing that the full constitution → specify → plan → tasks → implement workflow works from the terminal.

- **[Greenfield Spring Boot MVC with a custom preset](https://github.com/mnriem/spec-kit-pirate-speak-preset-demo)** — Builds a Spring Boot MVC application from scratch using a custom pirate-speak preset, demonstrating how presets can reshape the entire spec-kit experience: specifications become "Voyage Manifests," plans become "Battle Plans," and tasks become "Crew Assignments" — all generated in full pirate vernacular without changing any tooling.

## 🤖 Supported AI Agents

| Agent                                                                                | Support | Notes                                                                                                                                     |
| ------------------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| [Qoder CLI](https://qoder.com/cli)                                                   | ✅      |                                                                                                                                           |
| [Kiro CLI](https://kiro.dev/docs/cli/)                                               | ✅      | Use `--ai kiro-cli` (alias: `--ai kiro`)                                                                                                 |
| [Amp](https://ampcode.com/)                                                          | ✅      |                                                                                                                                           |
| [Auggie CLI](https://docs.augmentcode.com/cli/overview)                              | ✅      |                                                                                                                                           |
| [Claude Code](https://www.anthropic.com/claude-code)                                 | ✅      |                                                                                                                                           |
| [CodeBuddy CLI](https://www.codebuddy.ai/cli)                                        | ✅      |                                                                                                                                           |
| [Codex CLI](https://github.com/openai/codex)                                         | ✅      | Requires `--ai-skills`. Codex recommends [skills](https://developers.openai.com/codex/skills) and treats [custom prompts](https://developers.openai.com/codex/custom-prompts) as deprecated. Spec-kit installs Codex skills into `.agents/skills` and invokes them as `$speckit-<command>`. |
| [Cursor](https://cursor.sh/)                                                         | ✅      |                                                                                                                                           |
| [Gemini CLI](https://github.com/google-gemini/gemini-cli)                            | ✅      |                                                                                                                                           |
| [GitHub Copilot](https://code.visualstudio.com/)                                     | ✅      |                                                                                                                                           |
| [IBM Bob](https://www.ibm.com/products/bob)                                          | ✅      | IDE-based agent with slash command support                                                                                                |
| [Jules](https://jules.google.com/)                                                   | ✅      |                                                                                                                                           |
| [Kilo Code](https://github.com/Kilo-Org/kilocode)                                    | ✅      |                                                                                                                                           |
| [opencode](https://opencode.ai/)                                                     | ✅      |                                                                                                                                           |
| [Pi Coding Agent](https://pi.dev)                                                    | ✅      | Pi doesn't have MCP support out of the box, so `taskstoissues` won't work as intended. MCP support can be added via [extensions](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent#extensions) |
| [Qwen Code](https://github.com/QwenLM/qwen-code)                                     | ✅      |                                                                                                                                           |
| [Roo Code](https://roocode.com/)                                                     | ✅      |                                                                                                                                           |
| [SHAI (OVHcloud)](https://github.com/ovh/shai)                                       | ✅      |                                                                                                                                           |
| [Tabnine CLI](https://docs.tabnine.com/main/getting-started/tabnine-cli)             | ✅      |                                                                                                                                           |
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe)                            | ✅      |                                                                                                                                           |
| [Kimi Code](https://code.kimi.com/)                                                  | ✅      |                                                                                                                                           |
| [iFlow CLI](https://docs.iflow.cn/en/cli/quickstart)                                 | ✅      |                                                                                                                                           |
| [Windsurf](https://windsurf.com/)                                                    | ✅      |                                                                                                                                           |
| [Junie](https://junie.jetbrains.com/)                                                | ✅      |                                                                                                                                           |
| [Antigravity (agy)](https://antigravity.google/)                                     | ✅      | Requires `--ai-skills` |
| [Trae](https://www.trae.ai/)                                                         | ✅      |                                                                                                                                           |
| Generic                                                                              | ✅      | Bring your own agent — use `--ai generic --ai-commands-dir <path>` for unsupported agents                                                 |

## 🔧 Specify CLI Reference

The `specify` command supports the following options:

### Commands

| Command | Description                                                                                                                                                                                                                                                                              |
| ------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `init`  | Initialize a new Specify project from the latest template                                                                                                                                                                                                                                |
| `check` | Check for installed tools: `git` plus all CLI-based agents configured in `AGENT_CONFIG` (for example: `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `junie`, `qwen`, `opencode`, `codex`, `kiro-cli`, `shai`, `qodercli`, `vibe`, `kimi`, `iflow`, `pi`, etc.) |

### `specify init` Arguments & Options

| Argument/Option        | Type     | Description                                                                                                                                                                                                                                                                                                                                                                               |
| ---------------------- | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<project-name>`       | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory)                                                                                                                                                                                                                                                                                        |
| `--ai`                 | Option   | AI assistant to use (see `AGENT_CONFIG` for the full, up-to-date list). Common options include: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `junie`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `kiro-cli` (`kiro` alias), `agy`, `bob`, `qodercli`, `vibe`, `kimi`, `iflow`, `pi`, or `generic` (requires `--ai-commands-dir`) |
| `--ai-commands-dir`    | Option   | Directory for agent command files (required with `--ai generic`, e.g. `.myagent/commands/`)                                                                                                                                                                                                                                                                                               |
| `--script`             | Option   | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell)                                                                                                                                                                                                                                                                                                                               |
| `--ignore-agent-tools` | Flag     | Skip checks for AI agent tools like Claude Code                                                                                                                                                                                                                                                                                                                                           |
| `--no-git`             | Flag     | Skip git repository initialization                                                                                                                                                                                                                                                                                                                                                        |
| `--here`               | Flag     | Initialize project in the current directory instead of creating a new one                                                                                                                                                                                                                                                                                                                 |
| `--force`              | Flag     | Force merge/overwrite when initializing in current directory (skip confirmation)                                                                                                                                                                                                                                                                                                          |
| `--skip-tls`           | Flag     | Skip SSL/TLS verification (not recommended)                                                                                                                                                                                                                                                                                                                                               |
| `--debug`              | Flag     | Enable detailed debug output for troubleshooting                                                                                                                                                                                                                                                                                                                                          |
| `--github-token`       | Option   | GitHub token for API requests (or set GH_TOKEN/GITHUB_TOKEN env variable)                                                                                                                                                                                                                                                                                                                 |
| `--ai-skills`          | Flag     | Install Prompt.MD templates as agent skills in agent-specific `skills/` directory (requires `--ai`)                                                                                                                                                                                                                                                                                       |

### Examples

```bash
# Basic project initialization
specify init my-project

# Initialize with specific AI assistant
specify init my-project --ai claude

# Initialize with Cursor support
specify init my-project --ai cursor-agent

# Initialize with Qoder support
specify init my-project --ai qodercli

# Initialize with Windsurf support
specify init my-project --ai windsurf

# Initialize with Kiro CLI support
specify init my-project --ai kiro-cli

# Initialize with Amp support
specify init my-project --ai amp

# Initialize with SHAI support
specify init my-project --ai shai

# Initialize with Mistral Vibe support
specify init my-project --ai vibe

# Initialize with IBM Bob support
specify init my-project --ai bob

# Initialize with Pi Coding Agent support
specify init my-project --ai pi

# Initialize with Codex CLI support
specify init my-project --ai codex --ai-skills

# Initialize with Antigravity support
specify init my-project --ai agy --ai-skills

# Initialize with an unsupported agent (generic / bring your own agent)
specify init my-project --ai generic --ai-commands-dir .myagent/commands/

# Initialize with PowerShell scripts (Windows/cross-platform)
specify init my-project --ai copilot --script ps

# Initialize in current directory
specify init . --ai copilot
# or use the --here flag
specify init --here --ai copilot

# Force merge into current (non-empty) directory without confirmation
specify init . --force --ai copilot
# or
specify init --here --force --ai copilot

# Skip git initialization
specify init my-project --ai gemini --no-git

# Enable debug output for troubleshooting
specify init my-project --ai claude --debug

# Use GitHub token for API requests (helpful for corporate environments)
specify init my-project --ai claude --github-token ghp_your_token_here

# Install agent skills with the project
specify init my-project --ai claude --ai-skills

# Initialize in current directory with agent skills
specify init --here --ai gemini --ai-skills

# Check system requirements
specify check
```

### Available Slash Commands

After running `specify init`, your AI coding agent will have access to these slash commands for structured development.

For Codex CLI, `--ai-skills` installs spec-kit as agent skills instead of slash-command prompt files. In Codex skills mode, invoke spec-kit as `$speckit-constitution`, `$speckit-specify`, `$speckit-plan`, `$speckit-tasks`, and `$speckit-implement`.

#### Core Commands

Essential commands for the Spec-Driven Development workflow:

| Command                 | Description                                                              |
| ----------------------- | ------------------------------------------------------------------------ |
| `/speckit.constitution` | Create or update project governing principles and development guidelines |
| `/speckit.specify`      | Define what you want to build (requirements and user stories)            |
| `/speckit.plan`         | Create technical implementation plans with your chosen tech stack        |
| `/speckit.tasks`        | Generate actionable task lists for implementation                        |
| `/speckit.implement`    | Execute all tasks to build the feature according to the plan             |

#### Optional Commands

Additional commands for enhanced quality and validation:

| Command              | Description                                                                                                                          |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `/speckit.clarify`   | Clarify underspecified areas (recommended before `/speckit.plan`; formerly `/quizme`)                                                |
| `/speckit.analyze`   | Cross-artifact consistency & coverage analysis (run after `/speckit.tasks`, before `/speckit.implement`)                             |
| `/speckit.checklist` | Generate custom quality checklists that validate requirements completeness, clarity, and consistency (like "unit tests for English") |

### Environment Variables

| Variable          | Description                                                                                                                                                                                                                                                                                            |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches.<br/>\*\*Must be set in the context of the agent you're working with prior to using `/speckit.plan` or follow-up commands. |

## 🧩 Making Spec Kit Your Own: Extensions & Presets

Spec Kit can be tailored to your needs through two complementary systems — **extensions** and **presets** — plus project-local overrides for one-off adjustments:

```mermaid
block-beta
    columns 1
    overrides["⬆ Highest priority\nProject-Local Overrides\n.specify/templates/overrides/"]
    presets["Presets — Customize core & extensions\n.specify/presets/<preset-id>/templates/"]
    extensions["Extensions — Add new capabilities\n.specify/extensions/<ext-id>/templates/"]
    core["Spec Kit Core — Built-in SDD commands & templates\n.specify/templates/\n⬇ Lowest priority"]

    style overrides fill:transparent,stroke:#999
    style presets fill:transparent,stroke:#4a9eda
    style extensions fill:transparent,stroke:#4a9e4a
    style core fill:transparent,stroke:#e6a817
```

**Templates** are resolved at **runtime** — Spec Kit walks the stack top-down and uses the first match. Project-local overrides (`.specify/templates/overrides/`) let you make one-off adjustments for a single project without creating a full preset. **Commands** are applied at **install time** — when you run `specify extension add` or `specify preset add`, command files are written into agent directories (e.g., `.claude/commands/`). If multiple presets or extensions provide the same command, the highest-priority version wins. On removal, the next-highest-priority version is restored automatically. If no overrides or customizations exist, Spec Kit uses its core defaults.

### Extensions — Add New Capabilities

Use **extensions** when you need functionality that goes beyond Spec Kit's core. Extensions introduce new commands and templates — for example, adding domain-specific workflows that are not covered by the built-in SDD commands, integrating with external tools, or adding entirely new development phases. They expand *what Spec Kit can do*.

```bash
# Search available extensions
specify extension search

# Install an extension
specify extension add <extension-name>
```

For example, extensions could add Jira integration, post-implementation code review, V-Model test traceability, or project health diagnostics.

See the [Extensions README](./extensions/README.md) for the full guide, the complete community catalog, and how to build and publish your own.

### Presets — Customize Existing Workflows

Use **presets** when you want to change *how* Spec Kit works without adding new capabilities. Presets override the templates and commands that ship with the core *and* with installed extensions — for example, enforcing a compliance-oriented spec format, using domain-specific terminology, or applying organizational standards to plans and tasks. They customize the artifacts and instructions that Spec Kit and its extensions produce.

```bash
# Search available presets
specify preset search

# Install a preset
specify preset add <preset-name>
```

For example, presets could restructure spec templates to require regulatory traceability, adapt the workflow to fit the methodology you use (e.g., Agile, Kanban, Waterfall, jobs-to-be-done, or domain-driven design), add mandatory security review gates to plans, enforce test-first task ordering, or localize the entire workflow to a different language. The [pirate-speak demo](https://github.com/mnriem/spec-kit-pirate-speak-preset-demo) shows just how deep the customization can go. Multiple presets can be stacked with priority ordering.

See the [Presets README](./presets/README.md) for the full guide, including resolution order, priority, and how to create your own.

### When to Use Which

| Goal | Use |
| --- | --- |
| Add a brand-new command or workflow | Extension |
| Customize the format of specs, plans, or tasks | Preset |
| Integrate an external tool or service | Extension |
| Enforce organizational or regulatory standards | Preset |
| Ship reusable domain-specific templates | Either — presets for template overrides, extensions for templates bundled with new commands |

## 📚 Core Philosophy

Spec-Driven Development is a structured process that emphasizes:

- **Intent-driven development** where specifications define the "*what*" before the "*how*"
- **Rich specification creation** using guardrails and organizational principles
- **Multi-step refinement** rather than one-shot code generation from prompts
- **Heavy reliance** on advanced AI model capabilities for specification interpretation

## 🌟 Development Phases

| Phase                                    | Focus                    | Key Activities                                                                                                                                                     |
| ---------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **0-to-1 Development** ("Greenfield")    | Generate from scratch    | <ul><li>Start with high-level requirements</li><li>Generate specifications</li><li>Plan implementation steps</li><li>Build production-ready applications</li></ul> |
| **Creative Exploration**                 | Parallel implementations | <ul><li>Explore diverse solutions</li><li>Support multiple technology stacks & architectures</li><li>Experiment with UX patterns</li></ul>                         |
| **Iterative Enhancement** ("Brownfield") | Brownfield modernization | <ul><li>Add features iteratively</li><li>Modernize legacy systems</li><li>Adapt processes</li></ul>                                                                |

## 🎯 Experimental Goals

Our research and experimentation focus on:

### Technology independence

- Create applications using diverse technology stacks
- Validate the hypothesis that Spec-Driven Development is a process not tied to specific technologies, programming languages, or frameworks

### Enterprise constraints

- Demonstrate mission-critical application development
- Incorporate organizational constraints (cloud providers, tech stacks, engineering practices)
- Support enterprise design systems and compliance requirements

### User-centric development

- Build applications for different user cohorts and preferences
- Support various development approaches (from vibe-coding to AI-native development)

### Creative & iterative processes

- Validate the concept of parallel implementation exploration
- Provide robust iterative feature development workflows
- Extend processes to handle upgrades and modernization tasks

## 🔧 Prerequisites

- **Linux/macOS/Windows**
- [Supported](#-supported-ai-agents) AI coding agent.
- [uv](https://docs.astral.sh/uv/) for package management
- [Python 3.11+](https://www.python.org/downloads/)
- [Git](https://git-scm.com/downloads)

If you encounter issues with an agent, please open an issue so we can refine the integration.

## 📖 Learn More

- **[Complete Spec-Driven Development Methodology](./spec-driven.md)** - Deep dive into the full process
- **[Detailed Walkthrough](#-detailed-process)** - Step-by-step implementation guide

---

## 📋 Detailed Process

<details>
<summary>Click to expand the detailed step-by-step walkthrough</summary>

You can use the Specify CLI to bootstrap your project, which will bring in the required artifacts in your environment. Run:

```bash
specify init <project_name>
```

Or initialize in the current directory:

```bash
specify init .
# or use the --here flag
specify init --here
# Skip confirmation when the directory already has files
specify init . --force
# or
specify init --here --force
```

![Specify CLI bootstrapping a new project in the terminal](./media/specify_cli.gif)

You will be prompted to select the AI agent you are using. You can also proactively specify it directly in the terminal:

```bash
specify init <project_name> --ai claude
specify init <project_name> --ai gemini
specify init <project_name> --ai copilot

# Or in current directory:
specify init . --ai claude
specify init . --ai codex --ai-skills

# or use --here flag
specify init --here --ai claude
specify init --here --ai codex --ai-skills

# Force merge into a non-empty current directory
specify init . --force --ai claude

# or
specify init --here --force --ai claude
```

The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, Tabnine CLI, Kiro CLI, Pi, or Mistral Vibe installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:

```bash
specify init <project_name> --ai claude --ignore-agent-tools
```

### **STEP 1:** Establish project principles

Go to the project folder and run your AI agent. In our example, we're using `claude`.

![Bootstrapping Claude Code environment](./media/bootstrap-claude-code.gif)

You will know that things are configured correctly if you see the `/speckit.constitution`, `/speckit.specify`, `/speckit.plan`, `/speckit.tasks`, and `/speckit.implement` commands available.

The first step should be establishing your project's governing principles using the `/speckit.constitution` command. This helps ensure consistent decision-making throughout all subsequent development phases:

```text
/speckit.constitution Create principles focused on code quality, testing standards, user experience consistency, and performance requirements. Include governance for how these principles should guide technical decisions and implementation choices.
```

This step creates or updates the `.specify/memory/constitution.md` file with your project's foundational guidelines that the AI agent will reference during specification, planning, and implementation phases.

### **STEP 2:** Create project specifications

With your project principles established, you can now create the functional specifications. Use the `/speckit.specify` command and then provide the concrete requirements for the project you want to develop.

> [!IMPORTANT]
> Be as explicit as possible about *what* you are trying to build and *why*. **Do not focus on the tech stack at this point**.

An example prompt:

```text
Develop Taskify, a team productivity platform. It should allow users to create projects, add team members,
assign tasks, comment and move tasks between boards in Kanban style. In this initial phase for this feature,
let's call it "Create Taskify," let's have multiple users but the users will be declared ahead of time, predefined.
I want five users in two different categories, one product manager and four engineers. Let's create three
different sample projects. Let's have the standard Kanban columns for the status of each task, such as "To Do,"
"In Progress," "In Review," and "Done." There will be no login for this application as this is just the very
first testing thing to ensure that our basic features are set up. For each task in the UI for a task card,
you should be able to change the current status of the task between the different columns in the Kanban work board.
You should be able to leave an unlimited number of comments for a particular card. You should be able to, from that task
card, assign one of the valid users. When you first launch Taskify, it's going to give you a list of the five users to pick
from. There will be no password required. When you click on a user, you go into the main view, which displays the list of
projects. When you click on a project, you open the Kanban board for that project. You're going to see the columns.
You'll be able to drag and drop cards back and forth between different columns. You will see any cards that are
assigned to you, the currently logged in user, in a different color from all the other ones, so you can quickly
see yours. You can edit any comments that you make, but you can't edit comments that other people made. You can
delete any comments that you made, but you can't delete comments anybody else made.
```

After this prompt is entered, you should see Claude Code kick off the planning and spec drafting process. Claude Code will also trigger some of the built-in scripts to set up the repository.

Once this step is completed, you should have a new branch created (e.g., `001-create-taskify`), as well as a new specification in the `specs/001-create-taskify` directory.

The produced specification should contain a set of user stories and functional requirements, as defined in the template.

At this stage, your project folder contents should resemble the following:

```text
└── .specify
    ├── memory
    │  └── constitution.md
    ├── scripts
    │  ├── check-prerequisites.sh
    │  ├── common.sh
    │  ├── create-new-feature.sh
    │  ├── setup-plan.sh
    │  └── update-claude-md.sh
    ├── specs
    │  └── 001-create-taskify
    │      └── spec.md
    └── templates
        ├── plan-template.md
        ├── spec-template.md
        └── tasks-template.md
```

### **STEP 3:** Functional specification clarification (required before planning)

With the baseline specification created, you can go ahead and clarify any of the requirements that were not captured properly within the first shot attempt.

You should run the structured clarification workflow **before** creating a technical plan to reduce rework downstream.

Preferred order:

1. Use `/speckit.clarify` (structured) – sequential, coverage-based questioning that records answers in a Clarifications section.
2. Optionally follow up with ad-hoc free-form refinement if something still feels vague.

If you intentionally want to skip clarification (e.g., spike or exploratory prototype), explicitly state that so the agent doesn't block on missing clarifications.

Example free-form refinement prompt (after `/speckit.clarify` if still needed):

```text
For each sample project or project that you create there should be a variable number of tasks between 5 and 15
tasks for each one randomly distributed into different states of completion. Make sure that there's at least
one task in each stage of completion.
```

You should also ask Claude Code to validate the **Review & Acceptance Checklist**, checking off the things that are validated/pass the requirements, and leave the ones that are not unchecked. The following prompt can be used:

```text
Read the review and acceptance checklist, and check off each item in the checklist if the feature spec meets the criteria. Leave it empty if it does not.
```

It's important to use the interaction with Claude Code as an opportunity to clarify and ask questions around the specification - **do not treat its first attempt as final**.

### **STEP 4:** Generate a plan

You can now be specific about the tech stack and other technical requirements. You can use the `/speckit.plan` command that is built into the project template with a prompt like this:

```text
We are going to generate this using .NET Aspire, using Postgres as the database. The frontend should use
Blazor server with drag-and-drop task boards, real-time updates. There should be a REST API created with a projects API,
tasks API, and a notifications API.
```

The output of this step will include a number of implementation detail documents, with your directory tree resembling this:

```text
.
├── CLAUDE.md
├── memory
│  └── constitution.md
├── scripts
│  ├── check-prerequisites.sh
│  ├── common.sh
│  ├── create-new-feature.sh
│  ├── setup-plan.sh
│  └── update-claude-md.sh
├── specs
│  └── 001-create-taskify
│      ├── contracts
│      │  ├── api-spec.json
│      │  └── signalr-spec.md
│      ├── data-model.md
│      ├── plan.md
│      ├── quickstart.md
│      ├── research.md
│      └── spec.md
└── templates
    ├── CLAUDE-template.md
    ├── plan-template.md
    ├── spec-template.md
    └── tasks-template.md
```

Check the `research.md` document to ensure that the right tech stack is used, based on your instructions. You can ask Claude Code to refine it if any of the components stand out, or even have it check the locally-installed version of the platform/framework you want to use (e.g., .NET).

Additionally, you might want to ask Claude Code to research details about the chosen tech stack if it's something that is rapidly changing (e.g., .NET Aspire, JS frameworks), with a prompt like this:

```text
I want you to go through the implementation plan and implementation details, looking for areas that could
benefit from additional research as .NET Aspire is a rapidly changing library. For those areas that you identify that
require further research, I want you to update the research document with additional details about the specific
versions that we are going to be using in this Taskify application and spawn parallel research tasks to clarify
any details using research from the web.
```

During this process, you might find that Claude Code gets stuck researching the wrong thing - you can help nudge it in the right direction with a prompt like this:

```text
I think we need to break this down into a series of steps. First, identify a list of tasks
that you would need to do during implementation that you're not sure of or would benefit
from further research. Write down a list of those tasks. And then for each one of these tasks,
I want you to spin up a separate research task so that the net results is we are researching
all of those very specific tasks in parallel. What I saw you doing was it looks like you were
researching .NET Aspire in general and I don't think that's gonna do much for us in this case.
That's way too untargeted research. The research needs to help you solve a specific targeted question.
```

> [!NOTE]
> Claude Code might be over-eager and add components that you did not ask for. Ask it to clarify the rationale and the source of the change.

### **STEP 5:** Have Claude Code validate the plan

With the plan in place, you should have Claude Code run through it to make sure that there are no missing pieces. You can use a prompt like this:

```text
Now I want you to go and audit the implementation plan and the implementation detail files.
Read through it with an eye on determining whether or not there is a sequence of tasks that you need
to be doing that are obvious from reading this. Because I don't know if there's enough here. For example,
when I look at the core implementation, it would be useful to reference the appropriate places in the implementation
details where it can find the information as it walks through each step in the core implementation or in the refinement.
```

This helps refine the implementation plan and helps you avoid potential blind spots that Claude Code missed in its planning cycle. Once the initial refinement pass is complete, ask Claude Code to go through the checklist once more before you can get to the implementation.

You can also ask Claude Code (if you have the [GitHub CLI](https://docs.github.com/en/github-cli/github-cli) installed) to go ahead and create a pull request from your current branch to `main` with a detailed description, to make sure that the effort is properly tracked.

> [!NOTE]
> Before you have the agent implement it, it's also worth prompting Claude Code to cross-check the details to see if there are any over-engineered pieces (remember - it can be over-eager). If over-engineered components or decisions exist, you can ask Claude Code to resolve them. Ensure that Claude Code follows the [constitution](base/memory/constitution.md) as the foundational piece that it must adhere to when establishing the plan.

### **STEP 6:** Generate task breakdown with /speckit.tasks

With the implementation plan validated, you can now break down the plan into specific, actionable tasks that can be executed in the correct order. Use the `/speckit.tasks` command to automatically generate a detailed task breakdown from your implementation plan:

```text
/speckit.tasks
```

This step creates a `tasks.md` file in your feature specification directory that contains:

- **Task breakdown organized by user story** - Each user story becomes a separate implementation phase with its own set of tasks
- **Dependency management** - Tasks are ordered to respect dependencies between components (e.g., models before services, services before endpoints)
- **Parallel execution markers** - Tasks that can run in parallel are marked with `[P]` to optimize development workflow
- **File path specifications** - Each task includes the exact file paths where implementation should occur
- **Test-driven development structure** - If tests are requested, test tasks are included and ordered to be written before implementation
- **Checkpoint validation** - Each user story phase includes checkpoints to validate independent functionality

The generated tasks.md provides a clear roadmap for the `/speckit.implement` command, ensuring systematic implementation that maintains code quality and allows for incremental delivery of user stories.

### **STEP 7:** Implementation

Once ready, use the `/speckit.implement` command to execute your implementation plan:

```text
/speckit.implement
```

The `/speckit.implement` command will:

- Validate that all prerequisites are in place (constitution, spec, plan, and tasks)
- Parse the task breakdown from `tasks.md`
- Execute tasks in the correct order, respecting dependencies and parallel execution markers
- Follow the TDD approach defined in your task plan
- Provide progress updates and handle errors appropriately

> [!IMPORTANT]
> The AI agent will execute local CLI commands (such as `dotnet`, `npm`, etc.) - make sure you have the required tools installed on your machine.

Once the implementation is complete, test the application and resolve any r
Download .txt
gitextract_9kdterdf/

├── .devcontainer/
│   ├── devcontainer.json
│   └── post-create.sh
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── agent_request.yml
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── extension_submission.yml
│   │   ├── feature_request.yml
│   │   └── preset_submission.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── RELEASE-PROCESS.md
│       ├── codeql.yml
│       ├── docs.yml
│       ├── lint.yml
│       ├── release-trigger.yml
│       ├── release.yml
│       ├── scripts/
│       │   ├── check-release-exists.sh
│       │   ├── create-github-release.sh
│       │   ├── create-release-packages.ps1
│       │   ├── create-release-packages.sh
│       │   ├── generate-release-notes.sh
│       │   ├── get-next-version.sh
│       │   ├── simulate-release.sh
│       │   └── update-version.sh
│       ├── stale.yml
│       └── test.yml
├── .gitignore
├── .markdownlint-cli2.jsonc
├── AGENTS.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── docs/
│   ├── .gitignore
│   ├── README.md
│   ├── docfx.json
│   ├── index.md
│   ├── installation.md
│   ├── local-development.md
│   ├── quickstart.md
│   ├── toc.yml
│   └── upgrade.md
├── extensions/
│   ├── EXTENSION-API-REFERENCE.md
│   ├── EXTENSION-DEVELOPMENT-GUIDE.md
│   ├── EXTENSION-PUBLISHING-GUIDE.md
│   ├── EXTENSION-USER-GUIDE.md
│   ├── README.md
│   ├── RFC-EXTENSION-SYSTEM.md
│   ├── catalog.community.json
│   ├── catalog.json
│   ├── selftest/
│   │   ├── commands/
│   │   │   └── selftest.md
│   │   └── extension.yml
│   └── template/
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── EXAMPLE-README.md
│       ├── LICENSE
│       ├── README.md
│       ├── commands/
│       │   └── example.md
│       ├── config-template.yml
│       └── extension.yml
├── newsletters/
│   └── 2026-February.md
├── presets/
│   ├── ARCHITECTURE.md
│   ├── PUBLISHING.md
│   ├── README.md
│   ├── catalog.community.json
│   ├── catalog.json
│   ├── scaffold/
│   │   ├── README.md
│   │   ├── commands/
│   │   │   ├── speckit.myext.myextcmd.md
│   │   │   └── speckit.specify.md
│   │   ├── preset.yml
│   │   └── templates/
│   │       ├── myext-template.md
│   │       └── spec-template.md
│   └── self-test/
│       ├── commands/
│       │   └── speckit.specify.md
│       ├── preset.yml
│       └── templates/
│           ├── agent-file-template.md
│           ├── checklist-template.md
│           ├── constitution-template.md
│           ├── plan-template.md
│           ├── spec-template.md
│           └── tasks-template.md
├── pyproject.toml
├── scripts/
│   ├── bash/
│   │   ├── check-prerequisites.sh
│   │   ├── common.sh
│   │   ├── create-new-feature.sh
│   │   ├── setup-plan.sh
│   │   └── update-agent-context.sh
│   └── powershell/
│       ├── check-prerequisites.ps1
│       ├── common.ps1
│       ├── create-new-feature.ps1
│       ├── setup-plan.ps1
│       └── update-agent-context.ps1
├── spec-driven.md
├── spec-kit.code-workspace
├── src/
│   └── specify_cli/
│       ├── __init__.py
│       ├── agents.py
│       ├── extensions.py
│       └── presets.py
├── templates/
│   ├── agent-file-template.md
│   ├── checklist-template.md
│   ├── commands/
│   │   ├── analyze.md
│   │   ├── checklist.md
│   │   ├── clarify.md
│   │   ├── constitution.md
│   │   ├── implement.md
│   │   ├── plan.md
│   │   ├── specify.md
│   │   ├── tasks.md
│   │   └── taskstoissues.md
│   ├── constitution-template.md
│   ├── plan-template.md
│   ├── spec-template.md
│   ├── tasks-template.md
│   └── vscode-settings.json
└── tests/
    ├── __init__.py
    ├── hooks/
    │   ├── .specify/
    │   │   └── extensions.yml
    │   ├── TESTING.md
    │   ├── plan.md
    │   ├── spec.md
    │   └── tasks.md
    ├── test_agent_config_consistency.py
    ├── test_ai_skills.py
    ├── test_cursor_frontmatter.py
    ├── test_extensions.py
    ├── test_merge.py
    └── test_presets.py
Download .txt
SYMBOL INDEX (714 symbols across 10 files)

FILE: src/specify_cli/__init__.py
  function _github_token (line 62) | def _github_token(cli_token: str | None = None) -> str | None:
  function _github_auth_headers (line 66) | def _github_auth_headers(cli_token: str | None = None) -> dict:
  function _parse_rate_limit_headers (line 71) | def _parse_rate_limit_headers(headers: httpx.Headers) -> dict:
  function _format_rate_limit_error (line 99) | def _format_rate_limit_error(status_code: int, headers: httpx.Headers, u...
  function _build_ai_assistant_help (line 318) | def _build_ai_assistant_help() -> str:
  class StepTracker (line 356) | class StepTracker:
    method __init__ (line 360) | def __init__(self, title: str):
    method attach_refresh (line 366) | def attach_refresh(self, cb):
    method add (line 369) | def add(self, key: str, label: str):
    method start (line 374) | def start(self, key: str, detail: str = ""):
    method complete (line 377) | def complete(self, key: str, detail: str = ""):
    method error (line 380) | def error(self, key: str, detail: str = ""):
    method skip (line 383) | def skip(self, key: str, detail: str = ""):
    method _update (line 386) | def _update(self, key: str, status: str, detail: str):
    method _maybe_refresh (line 398) | def _maybe_refresh(self):
    method render (line 405) | def render(self):
  function get_key (line 441) | def get_key():
  function select_with_arrows (line 461) | def select_with_arrows(options: dict, prompt_text: str = "Select an opti...
  class BannerGroup (line 538) | class BannerGroup(TyperGroup):
    method format_help (line 541) | def format_help(self, ctx, formatter):
  function show_banner (line 555) | def show_banner():
  function callback (line 570) | def callback(ctx: typer.Context):
  function run_command (line 577) | def run_command(cmd: list[str], check_return: bool = True, capture: bool...
  function check_tool (line 595) | def check_tool(tool: str, tracker: StepTracker = None) -> bool:
  function is_git_repo (line 631) | def is_git_repo(path: Path = None) -> bool:
  function init_git_repo (line 651) | def init_git_repo(project_path: Path, quiet: bool = False) -> Tuple[bool...
  function handle_vscode_settings (line 686) | def handle_vscode_settings(sub_item, dest_file, rel_path, verbose=False,...
  function merge_json_files (line 755) | def merge_json_files(existing_path: Path, new_content: Any, verbose: boo...
  function download_template_from_github (line 834) | def download_template_from_github(ai_assistant: str, download_dir: Path,...
  function download_and_extract_template (line 948) | def download_and_extract_template(project_path: Path, ai_assistant: str,...
  function ensure_executable_scripts (line 1098) | def ensure_executable_scripts(project_path: Path, tracker: StepTracker |...
  function ensure_constitution_from_template (line 1146) | def ensure_constitution_from_template(project_path: Path, tracker: StepT...
  function save_init_options (line 1185) | def save_init_options(project_path: Path, options: dict[str, Any]) -> None:
  function load_init_options (line 1197) | def load_init_options(project_path: Path) -> dict[str, Any]:
  function _get_skills_dir (line 1237) | def _get_skills_dir(project_path: Path, selected_ai: str) -> Path:
  function install_ai_skills (line 1255) | def install_ai_skills(project_path: Path, selected_ai: str, tracker: Ste...
  function _has_bundled_skills (line 1426) | def _has_bundled_skills(project_path: Path, selected_ai: str) -> bool:
  function _handle_agent_skills_migration (line 1459) | def _handle_agent_skills_migration(console: Console, agent_key: str) -> ...
  function init (line 1468) | def init(
  function check (line 1929) | def check():
  function version (line 1973) | def version():
  function get_speckit_version (line 2083) | def get_speckit_version() -> str:
  function preset_list (line 2108) | def preset_list():
  function preset_add (line 2143) | def preset_add(
  function preset_remove (line 2251) | def preset_remove(
  function preset_search (line 2279) | def preset_search(
  function preset_resolve (line 2318) | def preset_resolve(
  function preset_info (line 2344) | def preset_info(
  function preset_set_priority (line 2418) | def preset_set_priority(
  function preset_enable (line 2470) | def preset_enable(
  function preset_disable (line 2511) | def preset_disable(
  function preset_catalog_list (line 2556) | def preset_catalog_list():
  function preset_catalog_add (line 2616) | def preset_catalog_add(
  function preset_catalog_remove (line 2688) | def preset_catalog_remove(
  function _resolve_installed_extension (line 2733) | def _resolve_installed_extension(
  function _resolve_catalog_extension (line 2790) | def _resolve_catalog_extension(
  function extension_list (line 2854) | def extension_list(
  function catalog_list (line 2898) | def catalog_list():
  function catalog_add (line 2958) | def catalog_add(
  function catalog_remove (line 3030) | def catalog_remove(
  function extension_add (line 3073) | def extension_add(
  function extension_remove (line 3217) | def extension_remove(
  function extension_search (line 3274) | def extension_search(
  function extension_info (line 3361) | def extension_info(
  function _print_extension_info (line 3453) | def _print_extension_info(ext_info: dict, manager):
  function extension_update (line 3553) | def extension_update(
  function extension_enable (line 3956) | def extension_enable(
  function extension_disable (line 4003) | def extension_disable(
  function extension_set_priority (line 4052) | def extension_set_priority(
  function main (line 4102) | def main():

FILE: src/specify_cli/agents.py
  class CommandRegistrar (line 16) | class CommandRegistrar:
    method parse_frontmatter (line 167) | def parse_frontmatter(content: str) -> tuple[dict, str]:
    method render_frontmatter (line 198) | def render_frontmatter(fm: dict) -> str:
    method _adjust_script_paths (line 213) | def _adjust_script_paths(self, frontmatter: dict) -> dict:
    method render_markdown_command (line 232) | def render_markdown_command(
    method render_toml_command (line 254) | def render_toml_command(
    method render_skill_command (line 286) | def render_skill_command(
    method _resolve_codex_skill_placeholders (line 321) | def _resolve_codex_skill_placeholders(frontmatter: dict, body: str, pr...
    method _convert_argument_placeholder (line 375) | def _convert_argument_placeholder(self, content: str, from_placeholder...
    method _compute_output_name (line 389) | def _compute_output_name(agent_name: str, cmd_name: str, agent_config:...
    method register_commands (line 400) | def register_commands(
    method write_copilot_prompt (line 490) | def write_copilot_prompt(project_root: Path, cmd_name: str) -> None:
    method register_commands_for_all_agents (line 502) | def register_commands_for_all_agents(
    method unregister_commands (line 540) | def unregister_commands(

FILE: src/specify_cli/extensions.py
  class ExtensionError (line 29) | class ExtensionError(Exception):
  class ValidationError (line 34) | class ValidationError(ExtensionError):
  class CompatibilityError (line 39) | class CompatibilityError(ExtensionError):
  function normalize_priority (line 44) | def normalize_priority(value: Any, default: int = 10) -> int:
  class CatalogEntry (line 65) | class CatalogEntry:
  class ExtensionManifest (line 74) | class ExtensionManifest:
    method __init__ (line 80) | def __init__(self, manifest_path: Path):
    method _load_yaml (line 93) | def _load_yaml(self, path: Path) -> dict:
    method _validate (line 103) | def _validate(self):
    method id (line 159) | def id(self) -> str:
    method name (line 164) | def name(self) -> str:
    method version (line 169) | def version(self) -> str:
    method description (line 174) | def description(self) -> str:
    method requires_speckit_version (line 179) | def requires_speckit_version(self) -> str:
    method commands (line 184) | def commands(self) -> List[Dict[str, Any]]:
    method hooks (line 189) | def hooks(self) -> Dict[str, Any]:
    method get_hash (line 193) | def get_hash(self) -> str:
  class ExtensionRegistry (line 199) | class ExtensionRegistry:
    method __init__ (line 205) | def __init__(self, extensions_dir: Path):
    method _load (line 215) | def _load(self) -> dict:
    method _save (line 243) | def _save(self):
    method add (line 249) | def add(self, extension_id: str, metadata: dict):
    method update (line 262) | def update(self, extension_id: str, metadata: dict):
    method restore (line 300) | def restore(self, extension_id: str, metadata: dict):
    method remove (line 322) | def remove(self, extension_id: str):
    method get (line 335) | def get(self, extension_id: str) -> Optional[dict]:
    method list (line 356) | def list(self) -> Dict[str, dict]:
    method keys (line 375) | def keys(self) -> set:
    method is_installed (line 389) | def is_installed(self, extension_id: str) -> bool:
    method list_by_priority (line 403) | def list_by_priority(self, include_disabled: bool = False) -> List[tup...
  class ExtensionManager (line 436) | class ExtensionManager:
    method __init__ (line 439) | def __init__(self, project_root: Path):
    method _load_extensionignore (line 450) | def _load_extensionignore(source_dir: Path) -> Optional[Callable[[str,...
    method check_compatibility (line 513) | def check_compatibility(
    method install_from_directory (line 547) | def install_from_directory(
    method install_from_zip (line 620) | def install_from_zip(
    method remove (line 680) | def remove(self, extension_id: str, keep_config: bool = False) -> bool:
    method list_installed (line 747) | def list_installed(self) -> List[Dict[str, Any]]:
    method get_extension (line 791) | def get_extension(self, extension_id: str) -> Optional[ExtensionManife...
  function version_satisfies (line 812) | def version_satisfies(current: str, required: str) -> bool:
  class CommandRegistrar (line 830) | class CommandRegistrar:
    method __init__ (line 842) | def __init__(self):
    method parse_frontmatter (line 848) | def parse_frontmatter(content: str) -> tuple[dict, str]:
    method render_frontmatter (line 853) | def render_frontmatter(fm: dict) -> str:
    method _write_copilot_prompt (line 858) | def _write_copilot_prompt(project_root, cmd_name: str) -> None:
    method _render_markdown_command (line 862) | def _render_markdown_command(self, frontmatter, body, ext_id):
    method _render_toml_command (line 867) | def _render_toml_command(self, frontmatter, body, ext_id):
    method register_commands_for_agent (line 873) | def register_commands_for_agent(
    method register_commands_for_all_agents (line 889) | def register_commands_for_all_agents(
    method unregister_commands (line 902) | def unregister_commands(
    method register_commands_for_claude (line 910) | def register_commands_for_claude(
  class ExtensionCatalog (line 920) | class ExtensionCatalog:
    method __init__ (line 927) | def __init__(self, project_root: Path):
    method _validate_catalog_url (line 939) | def _validate_catalog_url(self, url: str) -> None:
    method _load_catalog_config (line 960) | def _load_catalog_config(self, config_path: Path) -> Optional[List[Cat...
    method get_active_catalogs (line 1035) | def get_active_catalogs(self) -> List[CatalogEntry]:
    method get_catalog_url (line 1084) | def get_catalog_url(self) -> str:
    method _fetch_single_catalog (line 1099) | def _fetch_single_catalog(self, entry: CatalogEntry, force_refresh: bo...
    method _get_merged_extensions (line 1171) | def _get_merged_extensions(self, force_refresh: bool = False) -> List[...
    method is_cache_valid (line 1222) | def is_cache_valid(self) -> bool:
    method fetch_catalog (line 1241) | def fetch_catalog(self, force_refresh: bool = False) -> Dict[str, Any]:
    method search (line 1292) | def search(
    method get_extension_info (line 1347) | def get_extension_info(self, extension_id: str) -> Optional[Dict[str, ...
    method download_extension (line 1365) | def download_extension(self, extension_id: str, target_dir: Optional[P...
    method clear_cache (line 1421) | def clear_cache(self):
  class ConfigManager (line 1436) | class ConfigManager:
    method __init__ (line 1446) | def __init__(self, project_root: Path, extension_id: str):
    method _load_yaml_config (line 1457) | def _load_yaml_config(self, file_path: Path) -> Dict[str, Any]:
    method _get_extension_defaults (line 1474) | def _get_extension_defaults(self) -> Dict[str, Any]:
    method _get_project_config (line 1487) | def _get_project_config(self) -> Dict[str, Any]:
    method _get_local_config (line 1496) | def _get_local_config(self) -> Dict[str, Any]:
    method _get_env_config (line 1505) | def _get_env_config(self) -> Dict[str, Any]:
    method _merge_configs (line 1543) | def _merge_configs(self, base: Dict[str, Any], override: Dict[str, Any...
    method get_config (line 1565) | def get_config(self) -> Dict[str, Any]:
    method get_value (line 1588) | def get_value(self, key_path: str, default: Any = None) -> Any:
    method has_value (line 1614) | def has_value(self, key_path: str) -> bool:
  class HookExecutor (line 1635) | class HookExecutor:
    method __init__ (line 1638) | def __init__(self, project_root: Path):
    method get_project_config (line 1648) | def get_project_config(self) -> Dict[str, Any]:
    method save_project_config (line 1670) | def save_project_config(self, config: Dict[str, Any]):
    method register_hooks (line 1681) | def register_hooks(self, manifest: ExtensionManifest):
    method unregister_hooks (line 1731) | def unregister_hooks(self, extension_id: str):
    method get_hooks_for_event (line 1757) | def get_hooks_for_event(self, event_name: str) -> List[Dict[str, Any]]:
    method should_execute_hook (line 1772) | def should_execute_hook(self, hook: Dict[str, Any]) -> bool:
    method _evaluate_condition (line 1793) | def _evaluate_condition(self, condition: str, extension_id: Optional[s...
    method format_hook_message (line 1868) | def format_hook_message(
    method check_hooks_for_event (line 1907) | def check_hooks_for_event(self, event_name: str) -> Dict[str, Any]:
    method execute_hook (line 1949) | def execute_hook(self, hook: Dict[str, Any]) -> Dict[str, Any]:
    method enable_hooks (line 1973) | def enable_hooks(self, extension_id: str):
    method disable_hooks (line 1992) | def disable_hooks(self, extension_id: str):

FILE: src/specify_cli/presets.py
  class PresetCatalogEntry (line 31) | class PresetCatalogEntry:
  class PresetError (line 40) | class PresetError(Exception):
  class PresetValidationError (line 45) | class PresetValidationError(PresetError):
  class PresetCompatibilityError (line 50) | class PresetCompatibilityError(PresetError):
  class PresetManifest (line 58) | class PresetManifest:
    method __init__ (line 64) | def __init__(self, manifest_path: Path):
    method _load_yaml (line 77) | def _load_yaml(self, path: Path) -> dict:
    method _validate (line 87) | def _validate(self):
    method id (line 170) | def id(self) -> str:
    method name (line 175) | def name(self) -> str:
    method version (line 180) | def version(self) -> str:
    method description (line 185) | def description(self) -> str:
    method author (line 190) | def author(self) -> str:
    method requires_speckit_version (line 195) | def requires_speckit_version(self) -> str:
    method templates (line 200) | def templates(self) -> List[Dict[str, Any]]:
    method tags (line 205) | def tags(self) -> List[str]:
    method get_hash (line 209) | def get_hash(self) -> str:
  class PresetRegistry (line 215) | class PresetRegistry:
    method __init__ (line 221) | def __init__(self, packs_dir: Path):
    method _load (line 231) | def _load(self) -> dict:
    method _save (line 258) | def _save(self):
    method add (line 264) | def add(self, pack_id: str, metadata: dict):
    method remove (line 277) | def remove(self, pack_id: str):
    method update (line 290) | def update(self, pack_id: str, updates: dict):
    method restore (line 323) | def restore(self, pack_id: str, metadata: dict):
    method get (line 345) | def get(self, pack_id: str) -> Optional[dict]:
    method list (line 366) | def list(self) -> Dict[str, dict]:
    method keys (line 385) | def keys(self) -> set:
    method list_by_priority (line 399) | def list_by_priority(self, include_disabled: bool = False) -> List[tup...
    method is_installed (line 431) | def is_installed(self, pack_id: str) -> bool:
  class PresetManager (line 446) | class PresetManager:
    method __init__ (line 449) | def __init__(self, project_root: Path):
    method check_compatibility (line 459) | def check_compatibility(
    method _register_commands (line 494) | def _register_commands(
    method _unregister_commands (line 544) | def _unregister_commands(self, registered_commands: Dict[str, List[str...
    method _get_skills_dir (line 558) | def _get_skills_dir(self) -> Optional[Path]:
    method _register_skills (line 585) | def _register_skills(
    method _unregister_skills (line 705) | def _unregister_skills(self, skill_names: List[str], preset_dir: Path)...
    method install_from_directory (line 791) | def install_from_directory(
    method install_from_zip (line 850) | def install_from_zip(
    method remove (line 906) | def remove(self, pack_id: str) -> bool:
    method list_installed (line 936) | def list_installed(self) -> List[Dict[str, Any]]:
    method get_pack (line 979) | def get_pack(self, pack_id: str) -> Optional[PresetManifest]:
  class PresetCatalog (line 1000) | class PresetCatalog:
    method __init__ (line 1011) | def __init__(self, project_root: Path):
    method _validate_catalog_url (line 1023) | def _validate_catalog_url(self, url: str) -> None:
    method _load_catalog_config (line 1048) | def _load_catalog_config(self, config_path: Path) -> Optional[List[Pre...
    method get_active_catalogs (line 1113) | def get_active_catalogs(self) -> List[PresetCatalogEntry]:
    method get_catalog_url (line 1162) | def get_catalog_url(self) -> str:
    method _get_cache_paths (line 1174) | def _get_cache_paths(self, url: str):
    method _is_url_cache_valid (line 1191) | def _is_url_cache_valid(self, url: str) -> bool:
    method _fetch_single_catalog (line 1208) | def _fetch_single_catalog(self, entry: PresetCatalogEntry, force_refre...
    method _get_merged_packs (line 1259) | def _get_merged_packs(self, force_refresh: bool = False) -> Dict[str, ...
    method is_cache_valid (line 1281) | def is_cache_valid(self) -> bool:
    method fetch_catalog (line 1302) | def fetch_catalog(self, force_refresh: bool = False) -> Dict[str, Any]:
    method search (line 1358) | def search(
    method get_pack_info (line 1411) | def get_pack_info(
    method download_pack (line 1433) | def download_pack(
    method clear_cache (line 1503) | def clear_cache(self):
  class PresetResolver (line 1511) | class PresetResolver:
    method __init__ (line 1521) | def __init__(self, project_root: Path):
    method _get_all_extensions_by_priority (line 1533) | def _get_all_extensions_by_priority(self) -> list[tuple[int, str, dict...
    method resolve (line 1575) | def resolve(
    method resolve_with_source (line 1656) | def resolve_with_source(

FILE: tests/test_agent_config_consistency.py
  class TestAgentConfigConsistency (line 13) | class TestAgentConfigConsistency:
    method test_runtime_config_uses_kiro_cli_and_removes_q (line 16) | def test_runtime_config_uses_kiro_cli_and_removes_q(self):
    method test_extension_registrar_uses_kiro_cli_and_removes_q (line 23) | def test_extension_registrar_uses_kiro_cli_and_removes_q(self):
    method test_extension_registrar_includes_codex (line 31) | def test_extension_registrar_includes_codex(self):
    method test_runtime_codex_uses_native_skills (line 39) | def test_runtime_codex_uses_native_skills(self):
    method test_release_agent_lists_include_kiro_cli_and_exclude_q (line 44) | def test_release_agent_lists_include_kiro_cli_and_exclude_q(self):
    method test_release_ps_switch_has_shai_and_agy_generation (line 66) | def test_release_ps_switch_has_shai_and_agy_generation(self):
    method test_release_sh_switch_has_shai_and_agy_generation (line 73) | def test_release_sh_switch_has_shai_and_agy_generation(self):
    method test_release_scripts_generate_codex_skills (line 80) | def test_release_scripts_generate_codex_skills(self):
    method test_init_ai_help_includes_roo_and_kiro_alias (line 90) | def test_init_ai_help_includes_roo_and_kiro_alias(self):
    method test_devcontainer_kiro_installer_uses_pinned_checksum (line 97) | def test_devcontainer_kiro_installer_uses_pinned_checksum(self):
    method test_release_output_targets_kiro_prompt_dir (line 105) | def test_release_output_targets_kiro_prompt_dir(self):
    method test_agent_context_scripts_use_kiro_cli (line 121) | def test_agent_context_scripts_use_kiro_cli(self):
    method test_runtime_config_includes_tabnine (line 133) | def test_runtime_config_includes_tabnine(self):
    method test_extension_registrar_includes_tabnine (line 141) | def test_extension_registrar_includes_tabnine(self):
    method test_release_agent_lists_include_tabnine (line 152) | def test_release_agent_lists_include_tabnine(self):
    method test_release_scripts_generate_tabnine_toml_commands (line 168) | def test_release_scripts_generate_tabnine_toml_commands(self):
    method test_github_release_includes_tabnine_packages (line 177) | def test_github_release_includes_tabnine_packages(self):
    method test_agent_context_scripts_include_tabnine (line 184) | def test_agent_context_scripts_include_tabnine(self):
    method test_ai_help_includes_tabnine (line 194) | def test_ai_help_includes_tabnine(self):
    method test_kimi_in_agent_config (line 200) | def test_kimi_in_agent_config(self):
    method test_kimi_in_extension_registrar (line 207) | def test_kimi_in_extension_registrar(self):
    method test_kimi_in_release_agent_lists (line 216) | def test_kimi_in_release_agent_lists(self):
    method test_kimi_in_powershell_validate_set (line 232) | def test_kimi_in_powershell_validate_set(self):
    method test_kimi_in_github_release_output (line 242) | def test_kimi_in_github_release_output(self):
    method test_ai_help_includes_kimi (line 249) | def test_ai_help_includes_kimi(self):
    method test_trae_in_agent_config (line 255) | def test_trae_in_agent_config(self):
    method test_trae_in_extension_registrar (line 263) | def test_trae_in_extension_registrar(self):
    method test_trae_in_release_agent_lists (line 273) | def test_trae_in_release_agent_lists(self):
    method test_trae_in_release_scripts_generate_commands (line 289) | def test_trae_in_release_scripts_generate_commands(self):
    method test_trae_in_github_release_output (line 298) | def test_trae_in_github_release_output(self):
    method test_trae_in_agent_context_scripts (line 305) | def test_trae_in_agent_context_scripts(self):
    method test_trae_in_powershell_validate_set (line 315) | def test_trae_in_powershell_validate_set(self):
    method test_ai_help_includes_trae (line 325) | def test_ai_help_includes_trae(self):
    method test_pi_in_agent_config (line 331) | def test_pi_in_agent_config(self):
    method test_pi_in_extension_registrar (line 339) | def test_pi_in_extension_registrar(self):
    method test_pi_in_release_agent_lists (line 350) | def test_pi_in_release_agent_lists(self):
    method test_release_scripts_generate_pi_prompt_templates (line 366) | def test_release_scripts_generate_pi_prompt_templates(self):
    method test_pi_in_powershell_validate_set (line 376) | def test_pi_in_powershell_validate_set(self):
    method test_pi_in_github_release_output (line 386) | def test_pi_in_github_release_output(self):
    method test_agent_context_scripts_include_pi (line 393) | def test_agent_context_scripts_include_pi(self):
    method test_ai_help_includes_pi (line 403) | def test_ai_help_includes_pi(self):
    method test_iflow_in_agent_config (line 409) | def test_iflow_in_agent_config(self):
    method test_iflow_in_extension_registrar (line 416) | def test_iflow_in_extension_registrar(self):
    method test_iflow_in_release_agent_lists (line 425) | def test_iflow_in_release_agent_lists(self):
    method test_iflow_in_release_scripts_build_variant (line 441) | def test_iflow_in_release_scripts_build_variant(self):
    method test_iflow_in_github_release_output (line 450) | def test_iflow_in_github_release_output(self):
    method test_iflow_in_agent_context_scripts (line 457) | def test_iflow_in_agent_context_scripts(self):
    method test_ai_help_includes_iflow (line 467) | def test_ai_help_includes_iflow(self):

FILE: tests/test_ai_skills.py
  function temp_dir (line 37) | def temp_dir():
  function project_dir (line 45) | def project_dir(temp_dir):
  function templates_dir (line 53) | def templates_dir(project_dir):
  function commands_dir_claude (line 116) | def commands_dir_claude(project_dir):
  function commands_dir_gemini (line 126) | def commands_dir_gemini(project_dir):
  function commands_dir_qwen (line 136) | def commands_dir_qwen(project_dir):
  class TestGetSkillsDir (line 147) | class TestGetSkillsDir:
    method test_claude_skills_dir (line 150) | def test_claude_skills_dir(self, project_dir):
    method test_gemini_skills_dir (line 155) | def test_gemini_skills_dir(self, project_dir):
    method test_tabnine_skills_dir (line 160) | def test_tabnine_skills_dir(self, project_dir):
    method test_copilot_skills_dir (line 165) | def test_copilot_skills_dir(self, project_dir):
    method test_codex_uses_override (line 170) | def test_codex_uses_override(self, project_dir):
    method test_cursor_agent_skills_dir (line 175) | def test_cursor_agent_skills_dir(self, project_dir):
    method test_kiro_cli_skills_dir (line 180) | def test_kiro_cli_skills_dir(self, project_dir):
    method test_pi_skills_dir (line 185) | def test_pi_skills_dir(self, project_dir):
    method test_unknown_agent_uses_default (line 190) | def test_unknown_agent_uses_default(self, project_dir):
    method test_all_configured_agents_resolve (line 195) | def test_all_configured_agents_resolve(self, project_dir):
    method test_override_takes_precedence_over_config (line 204) | def test_override_takes_precedence_over_config(self, project_dir):
  class TestInstallAiSkills (line 214) | class TestInstallAiSkills:
    method test_skills_installed_with_correct_structure (line 217) | def test_skills_installed_with_correct_structure(self, project_dir, te...
    method test_generated_skill_has_parseable_yaml (line 251) | def test_generated_skill_has_parseable_yaml(self, project_dir, templat...
    method test_empty_yaml_frontmatter (line 268) | def test_empty_yaml_frontmatter(self, project_dir, templates_dir):
    method test_enhanced_descriptions_used_when_available (line 280) | def test_enhanced_descriptions_used_when_available(self, project_dir, ...
    method test_template_without_frontmatter (line 295) | def test_template_without_frontmatter(self, project_dir, templates_dir):
    method test_missing_templates_directory (line 307) | def test_missing_templates_directory(self, project_dir):
    method test_empty_templates_directory (line 323) | def test_empty_templates_directory(self, project_dir):
    method test_malformed_yaml_frontmatter (line 339) | def test_malformed_yaml_frontmatter(self, project_dir):
    method test_additive_does_not_overwrite_other_files (line 361) | def test_additive_does_not_overwrite_other_files(self, project_dir, te...
    method test_return_value (line 375) | def test_return_value(self, project_dir, templates_dir):
    method test_return_false_when_no_templates (line 379) | def test_return_false_when_no_templates(self, project_dir):
    method test_non_md_commands_dir_falls_back (line 388) | def test_non_md_commands_dir_falls_back(self, project_dir):
    method test_qwen_md_commands_dir_installs_skills (line 408) | def test_qwen_md_commands_dir_installs_skills(self, project_dir):
    method test_pi_prompt_dir_installs_skills (line 430) | def test_pi_prompt_dir_installs_skills(self, project_dir):
    method test_skills_install_for_all_agents (line 452) | def test_skills_install_for_all_agents(self, temp_dir, agent_key):
    method test_copilot_ignores_non_speckit_agents (line 479) | def test_copilot_ignores_non_speckit_agents(self, project_dir):
    method test_non_speckit_commands_ignored_for_all_agents (line 505) | def test_non_speckit_commands_ignored_for_all_agents(self, temp_dir, a...
    method test_copilot_fallback_when_only_non_speckit_agents (line 530) | def test_copilot_fallback_when_only_non_speckit_agents(self, project_d...
    method test_fallback_when_only_non_speckit_commands (line 551) | def test_fallback_when_only_non_speckit_commands(self, temp_dir, agent...
  class TestCommandCoexistence (line 574) | class TestCommandCoexistence:
    method test_existing_commands_preserved_claude (line 582) | def test_existing_commands_preserved_claude(self, project_dir, templat...
    method test_existing_commands_preserved_gemini (line 595) | def test_existing_commands_preserved_gemini(self, project_dir, templat...
    method test_existing_commands_preserved_qwen (line 604) | def test_existing_commands_preserved_qwen(self, project_dir, templates...
    method test_commands_dir_not_removed (line 613) | def test_commands_dir_not_removed(self, project_dir, templates_dir, co...
    method test_no_commands_dir_no_error (line 619) | def test_no_commands_dir_no_error(self, project_dir, templates_dir):
  class TestNewProjectCommandSkip (line 629) | class TestNewProjectCommandSkip:
    method _fake_extract (line 636) | def _fake_extract(self, agent, project_path, **_kwargs):
    method test_new_project_commands_removed_after_skills_succeed (line 646) | def test_new_project_commands_removed_after_skills_succeed(self, tmp_p...
    method test_new_project_nonstandard_commands_subdir_removed_after_skills_succeed (line 672) | def test_new_project_nonstandard_commands_subdir_removed_after_skills_...
    method test_codex_native_skills_preserved_without_conversion (line 696) | def test_codex_native_skills_preserved_without_conversion(self, tmp_pa...
    method test_codex_native_skills_missing_fails_clearly (line 723) | def test_codex_native_skills_missing_fails_clearly(self, tmp_path):
    method test_codex_native_skills_ignores_non_speckit_skill_dirs (line 745) | def test_codex_native_skills_ignores_non_speckit_skill_dirs(self, tmp_...
    method test_commands_preserved_when_skills_fail (line 772) | def test_commands_preserved_when_skills_fail(self, tmp_path):
    method test_here_mode_commands_preserved (line 796) | def test_here_mode_commands_preserved(self, tmp_path, monkeypatch):
  class TestSkipIfExists (line 831) | class TestSkipIfExists:
    method test_existing_skill_not_overwritten (line 834) | def test_existing_skill_not_overwritten(self, project_dir, templates_d...
    method test_fresh_install_writes_all_skills (line 852) | def test_fresh_install_writes_all_skills(self, project_dir, templates_...
  class TestSkillDescriptions (line 865) | class TestSkillDescriptions:
    method test_all_known_commands_have_descriptions (line 868) | def test_all_known_commands_have_descriptions(self):
  class TestCliValidation (line 881) | class TestCliValidation:
    method test_ai_skills_without_ai_fails (line 884) | def test_ai_skills_without_ai_fails(self):
    method test_ai_skills_without_ai_shows_usage (line 894) | def test_ai_skills_without_ai_shows_usage(self):
    method test_agy_without_ai_skills_fails (line 904) | def test_agy_without_ai_skills_fails(self):
    method test_codex_without_ai_skills_fails (line 915) | def test_codex_without_ai_skills_fails(self):
    method test_interactive_agy_without_ai_skills_prompts_skills (line 926) | def test_interactive_agy_without_ai_skills_prompts_skills(self, monkey...
    method test_interactive_codex_without_ai_skills_enables_skills (line 968) | def test_interactive_codex_without_ai_skills_enables_skills(self, monk...
    method test_kimi_next_steps_show_skill_invocation (line 1010) | def test_kimi_next_steps_show_skill_invocation(self, monkeypatch):
    method test_ai_skills_flag_appears_in_help (line 1034) | def test_ai_skills_flag_appears_in_help(self):
    method test_kiro_alias_normalized_to_kiro_cli (line 1045) | def test_kiro_alias_normalized_to_kiro_cli(self, tmp_path):
    method test_q_removed_from_agent_config (line 1076) | def test_q_removed_from_agent_config(self):
  class TestParameterOrderingIssue (line 1082) | class TestParameterOrderingIssue:
    method test_ai_flag_consuming_here_flag (line 1085) | def test_ai_flag_consuming_here_flag(self):
    method test_ai_flag_consuming_ai_skills_flag (line 1097) | def test_ai_flag_consuming_ai_skills_flag(self):
    method test_error_message_provides_hint (line 1109) | def test_error_message_provides_hint(self):
    method test_error_message_lists_available_agents (line 1120) | def test_error_message_lists_available_agents(self):
    method test_ai_commands_dir_consuming_flag (line 1132) | def test_ai_commands_dir_consuming_flag(self):

FILE: tests/test_cursor_frontmatter.py
  class TestScriptFrontmatterPattern (line 37) | class TestScriptFrontmatterPattern:
    method test_create_new_has_mdc_frontmatter_logic (line 40) | def test_create_new_has_mdc_frontmatter_logic(self):
    method test_update_existing_has_mdc_frontmatter_logic (line 47) | def test_update_existing_has_mdc_frontmatter_logic(self):
    method test_powershell_script_has_mdc_frontmatter_logic (line 57) | def test_powershell_script_has_mdc_frontmatter_logic(self):
  class TestCursorFrontmatterIntegration (line 76) | class TestCursorFrontmatterIntegration:
    method git_repo (line 80) | def git_repo(self, tmp_path):
    method _run_update (line 163) | def _run_update(self, repo, agent_type="cursor-agent"):
    method test_new_mdc_file_has_frontmatter (line 175) | def test_new_mdc_file_has_frontmatter(self, git_repo):
    method test_existing_mdc_without_frontmatter_gets_it_added (line 196) | def test_existing_mdc_without_frontmatter_gets_it_added(self, git_repo):
    method test_existing_mdc_with_frontmatter_not_duplicated (line 221) | def test_existing_mdc_with_frontmatter_not_duplicated(self, git_repo):
    method test_non_mdc_file_has_no_frontmatter (line 253) | def test_non_mdc_file_has_no_frontmatter(self, git_repo):

FILE: tests/test_extensions.py
  function temp_dir (line 37) | def temp_dir():
  function valid_manifest_data (line 45) | def valid_manifest_data():
  function extension_dir (line 83) | def extension_dir(temp_dir, valid_manifest_data):
  function project_dir (line 113) | def project_dir(temp_dir):
  class TestNormalizePriority (line 127) | class TestNormalizePriority:
    method test_valid_integer (line 130) | def test_valid_integer(self):
    method test_valid_string_number (line 136) | def test_valid_string_number(self):
    method test_zero_returns_default (line 141) | def test_zero_returns_default(self):
    method test_negative_returns_default (line 146) | def test_negative_returns_default(self):
    method test_none_returns_default (line 151) | def test_none_returns_default(self):
    method test_invalid_string_returns_default (line 156) | def test_invalid_string_returns_default(self):
    method test_float_truncates (line 161) | def test_float_truncates(self):
    method test_empty_string_returns_default (line 166) | def test_empty_string_returns_default(self):
    method test_custom_default (line 170) | def test_custom_default(self):
  class TestExtensionManifest (line 178) | class TestExtensionManifest:
    method test_valid_manifest (line 181) | def test_valid_manifest(self, extension_dir):
    method test_missing_required_field (line 193) | def test_missing_required_field(self, temp_dir):
    method test_invalid_extension_id (line 204) | def test_invalid_extension_id(self, temp_dir, valid_manifest_data):
    method test_invalid_version (line 217) | def test_invalid_version(self, temp_dir, valid_manifest_data):
    method test_invalid_command_name (line 230) | def test_invalid_command_name(self, temp_dir, valid_manifest_data):
    method test_no_commands (line 243) | def test_no_commands(self, temp_dir, valid_manifest_data):
    method test_manifest_hash (line 256) | def test_manifest_hash(self, extension_dir):
  class TestExtensionRegistry (line 268) | class TestExtensionRegistry:
    method test_empty_registry (line 271) | def test_empty_registry(self, temp_dir):
    method test_add_extension (line 282) | def test_add_extension(self, temp_dir):
    method test_remove_extension (line 301) | def test_remove_extension(self, temp_dir):
    method test_registry_persistence (line 316) | def test_registry_persistence(self, temp_dir):
    method test_update_preserves_installed_at (line 332) | def test_update_preserves_installed_at(self, temp_dir):
    method test_update_merges_with_existing (line 353) | def test_update_merges_with_existing(self, temp_dir):
    method test_update_raises_for_missing_extension (line 374) | def test_update_raises_for_missing_extension(self, temp_dir):
    method test_restore_overwrites_completely (line 384) | def test_restore_overwrites_completely(self, temp_dir):
    method test_restore_can_recreate_removed_entry (line 405) | def test_restore_can_recreate_removed_entry(self, temp_dir):
    method test_restore_rejects_none_metadata (line 423) | def test_restore_rejects_none_metadata(self, temp_dir):
    method test_restore_rejects_non_dict_metadata (line 432) | def test_restore_rejects_non_dict_metadata(self, temp_dir):
    method test_restore_uses_deep_copy (line 444) | def test_restore_uses_deep_copy(self, temp_dir):
    method test_get_returns_deep_copy (line 465) | def test_get_returns_deep_copy(self, temp_dir):
    method test_get_returns_none_for_corrupted_entry (line 484) | def test_get_returns_none_for_corrupted_entry(self, temp_dir):
    method test_list_returns_deep_copy (line 504) | def test_list_returns_deep_copy(self, temp_dir):
    method test_list_returns_empty_dict_for_corrupted_registry (line 523) | def test_list_returns_empty_dict_for_corrupted_registry(self, temp_dir):
  class TestExtensionManager (line 540) | class TestExtensionManager:
    method test_check_compatibility_valid (line 543) | def test_check_compatibility_valid(self, extension_dir, project_dir):
    method test_check_compatibility_invalid (line 552) | def test_check_compatibility_invalid(self, extension_dir, project_dir):
    method test_install_from_directory (line 561) | def test_install_from_directory(self, extension_dir, project_dir):
    method test_install_duplicate (line 580) | def test_install_duplicate(self, extension_dir, project_dir):
    method test_remove_extension (line 591) | def test_remove_extension(self, extension_dir, project_dir):
    method test_remove_nonexistent (line 608) | def test_remove_nonexistent(self, project_dir):
    method test_list_installed (line 615) | def test_list_installed(self, extension_dir, project_dir):
    method test_config_backup_on_remove (line 634) | def test_config_backup_on_remove(self, extension_dir, project_dir):
  class TestCommandRegistrar (line 658) | class TestCommandRegistrar:
    method test_kiro_cli_agent_config_present (line 661) | def test_kiro_cli_agent_config_present(self):
    method test_codex_agent_config_present (line 667) | def test_codex_agent_config_present(self):
    method test_pi_agent_config_present (line 673) | def test_pi_agent_config_present(self):
    method test_qwen_agent_config_is_markdown (line 682) | def test_qwen_agent_config_is_markdown(self):
    method test_parse_frontmatter_valid (line 691) | def test_parse_frontmatter_valid(self):
    method test_parse_frontmatter_no_frontmatter (line 711) | def test_parse_frontmatter_no_frontmatter(self):
    method test_parse_frontmatter_non_mapping_returns_empty_dict (line 721) | def test_parse_frontmatter_non_mapping_returns_empty_dict(self):
    method test_render_frontmatter (line 736) | def test_render_frontmatter(self):
    method test_register_commands_for_claude (line 750) | def test_register_commands_for_claude(self, extension_dir, project_dir):
    method test_command_with_aliases (line 778) | def test_command_with_aliases(self, project_dir, temp_dir):
    method test_unregister_commands_for_codex_skills_uses_mapped_names (line 827) | def test_unregister_commands_for_codex_skills_uses_mapped_names(self, ...
    method test_register_commands_for_all_agents_distinguishes_codex_from_amp (line 844) | def test_register_commands_for_all_agents_distinguishes_codex_from_amp...
    method test_codex_skill_registration_writes_skill_frontmatter (line 857) | def test_codex_skill_registration_writes_skill_frontmatter(self, exten...
    method test_codex_skill_registration_resolves_script_placeholders (line 877) | def test_codex_skill_registration_resolves_script_placeholders(self, p...
    method test_codex_skill_alias_frontmatter_matches_alias_name (line 946) | def test_codex_skill_alias_frontmatter_matches_alias_name(self, projec...
    method test_codex_skill_registration_uses_fallback_script_variant_without_init_options (line 993) | def test_codex_skill_registration_uses_fallback_script_variant_without...
    method test_codex_skill_registration_fallback_prefers_powershell_on_windows (line 1056) | def test_codex_skill_registration_fallback_prefers_powershell_on_windows(
    method test_register_commands_for_copilot (line 1120) | def test_register_commands_for_copilot(self, extension_dir, project_dir):
    method test_copilot_companion_prompt_created (line 1148) | def test_copilot_companion_prompt_created(self, extension_dir, project...
    method test_copilot_aliases_get_companion_prompts (line 1168) | def test_copilot_aliases_get_companion_prompts(self, project_dir, temp...
    method test_non_copilot_agent_no_companion_file (line 1219) | def test_non_copilot_agent_no_companion_file(self, extension_dir, proj...
  class TestVersionSatisfies (line 1238) | class TestVersionSatisfies:
    method test_version_satisfies_simple (line 1241) | def test_version_satisfies_simple(self):
    method test_version_satisfies_range (line 1247) | def test_version_satisfies_range(self):
    method test_version_satisfies_complex (line 1253) | def test_version_satisfies_complex(self):
    method test_version_satisfies_invalid (line 1258) | def test_version_satisfies_invalid(self):
  class TestIntegration (line 1266) | class TestIntegration:
    method test_full_install_and_remove_workflow (line 1269) | def test_full_install_and_remove_workflow(self, extension_dir, project...
    method test_copilot_cleanup_removes_prompt_files (line 1311) | def test_copilot_cleanup_removes_prompt_files(self, extension_dir, pro...
    method test_multiple_extensions (line 1336) | def test_multiple_extensions(self, temp_dir, project_dir):
  class TestExtensionCatalog (line 1393) | class TestExtensionCatalog:
    method test_catalog_initialization (line 1396) | def test_catalog_initialization(self, temp_dir):
    method test_cache_directory_creation (line 1407) | def test_cache_directory_creation(self, temp_dir):
    method test_cache_expiration (line 1444) | def test_cache_expiration(self, temp_dir):
    method test_search_all_extensions (line 1472) | def test_search_all_extensions(self, temp_dir):
    method test_search_by_query (line 1540) | def test_search_by_query(self, temp_dir):
    method test_search_by_tag (line 1604) | def test_search_by_tag(self, temp_dir):
    method test_search_verified_only (line 1675) | def test_search_verified_only(self, temp_dir):
    method test_get_extension_info (line 1739) | def test_get_extension_info(self, temp_dir):
    method test_clear_cache (line 1801) | def test_clear_cache(self, temp_dir):
  class TestCatalogEntry (line 1826) | class TestCatalogEntry:
    method test_catalog_entry_creation (line 1829) | def test_catalog_entry_creation(self):
  class TestCatalogStack (line 1845) | class TestCatalogStack:
    method _make_project (line 1848) | def _make_project(self, temp_dir: Path) -> Path:
    method _write_catalog_config (line 1855) | def _write_catalog_config(self, project_dir: Path, catalogs: list) -> ...
    method _write_valid_cache (line 1863) | def _write_valid_cache(
    method test_default_stack (line 1881) | def test_default_stack(self, temp_dir):
    method test_env_var_overrides_default_stack (line 1898) | def test_env_var_overrides_default_stack(self, temp_dir, monkeypatch):
    method test_env_var_invalid_url_raises (line 1911) | def test_env_var_invalid_url_raises(self, temp_dir, monkeypatch):
    method test_project_config_overrides_defaults (line 1920) | def test_project_config_overrides_defaults(self, temp_dir):
    method test_project_config_sorted_by_priority (line 1942) | def test_project_config_sorted_by_priority(self, temp_dir):
    method test_project_config_invalid_url_raises (line 1970) | def test_project_config_invalid_url_raises(self, temp_dir):
    method test_empty_project_config_raises_error (line 1989) | def test_empty_project_config_raises_error(self, temp_dir):
    method test_catalog_entries_without_urls_raises_error (line 2005) | def test_catalog_entries_without_urls_raises_error(self, temp_dir):
    method test_load_catalog_config_missing_file (line 2028) | def test_load_catalog_config_missing_file(self, temp_dir):
    method test_load_catalog_config_localhost_allowed (line 2036) | def test_load_catalog_config_localhost_allowed(self, temp_dir):
    method test_merge_conflict_higher_priority_wins (line 2059) | def test_merge_conflict_higher_priority_wins(self, temp_dir):
    method test_install_allowed_false_from_get_extension_info (line 2144) | def test_install_allowed_false_from_get_extension_info(self, temp_dir):
    method test_search_results_include_catalog_metadata (line 2179) | def test_search_results_include_catalog_metadata(self, temp_dir):
  class TestExtensionIgnore (line 2213) | class TestExtensionIgnore:
    method _make_extension (line 2216) | def _make_extension(self, temp_dir, valid_manifest_data, extra_files=N...
    method test_no_extensionignore (line 2251) | def test_no_extensionignore(self, temp_dir, valid_manifest_data):
    method test_extensionignore_excludes_files (line 2270) | def test_extensionignore_excludes_files(self, temp_dir, valid_manifest...
    method test_extensionignore_glob_patterns (line 2300) | def test_extensionignore_glob_patterns(self, temp_dir, valid_manifest_...
    method test_extensionignore_comments_and_blanks (line 2325) | def test_extensionignore_comments_and_blanks(self, temp_dir, valid_man...
    method test_extensionignore_itself_excluded (line 2345) | def test_extensionignore_itself_excluded(self, temp_dir, valid_manifes...
    method test_extensionignore_relative_path_match (line 2364) | def test_extensionignore_relative_path_match(self, temp_dir, valid_man...
    method test_extensionignore_dotdot_pattern_is_noop (line 2388) | def test_extensionignore_dotdot_pattern_is_noop(self, temp_dir, valid_...
    method test_extensionignore_absolute_path_pattern_is_noop (line 2410) | def test_extensionignore_absolute_path_pattern_is_noop(self, temp_dir,...
    method test_extensionignore_empty_file (line 2431) | def test_extensionignore_empty_file(self, temp_dir, valid_manifest_data):
    method test_extensionignore_windows_backslash_patterns (line 2454) | def test_extensionignore_windows_backslash_patterns(self, temp_dir, va...
    method test_extensionignore_star_does_not_cross_directories (line 2477) | def test_extensionignore_star_does_not_cross_directories(self, temp_di...
    method test_extensionignore_doublestar_crosses_directories (line 2501) | def test_extensionignore_doublestar_crosses_directories(self, temp_dir...
    method test_extensionignore_negation_pattern (line 2526) | def test_extensionignore_negation_pattern(self, temp_dir, valid_manife...
  class TestExtensionAddCLI (line 2553) | class TestExtensionAddCLI:
    method test_add_by_display_name_uses_resolved_id_for_download (line 2556) | def test_add_by_display_name_uses_resolved_id_for_download(self, tmp_p...
  class TestExtensionUpdateCLI (line 2612) | class TestExtensionUpdateCLI:
    method _create_extension_source (line 2616) | def _create_extension_source(base_dir: Path, version: str, include_con...
    method _create_catalog_zip (line 2658) | def _create_catalog_zip(zip_path: Path, version: str):
    method test_update_success_preserves_installed_at (line 2678) | def test_update_success_preserves_installed_at(self, tmp_path):
    method test_update_failure_rolls_back_registry_hooks_and_commands (line 2726) | def test_update_failure_rolls_back_registry_hooks_and_commands(self, t...
  class TestExtensionListCLI (line 2788) | class TestExtensionListCLI:
    method test_list_shows_extension_id (line 2791) | def test_list_shows_extension_id(self, extension_dir, project_dir):
  class TestExtensionPriority (line 2814) | class TestExtensionPriority:
    method test_list_by_priority_empty (line 2817) | def test_list_by_priority_empty(self, temp_dir):
    method test_list_by_priority_single (line 2827) | def test_list_by_priority_single(self, temp_dir):
    method test_list_by_priority_ordering (line 2841) | def test_list_by_priority_ordering(self, temp_dir):
    method test_list_by_priority_default (line 2860) | def test_list_by_priority_default(self, temp_dir):
    method test_list_by_priority_invalid_priority_defaults (line 2879) | def test_list_by_priority_invalid_priority_defaults(self, temp_dir):
    method test_list_by_priority_excludes_disabled (line 2897) | def test_list_by_priority_excludes_disabled(self, temp_dir):
    method test_list_by_priority_includes_disabled_when_requested (line 2914) | def test_list_by_priority_includes_disabled_when_requested(self, temp_...
    method test_install_with_priority (line 2931) | def test_install_with_priority(self, extension_dir, project_dir):
    method test_install_default_priority (line 2939) | def test_install_default_priority(self, extension_dir, project_dir):
    method test_list_installed_includes_priority (line 2947) | def test_list_installed_includes_priority(self, extension_dir, project...
    method test_priority_preserved_on_update (line 2957) | def test_priority_preserved_on_update(self, temp_dir):
    method test_corrupted_extension_entry_not_picked_up_as_unregistered (line 2972) | def test_corrupted_extension_entry_not_picked_up_as_unregistered(self,...
  class TestExtensionPriorityCLI (line 3003) | class TestExtensionPriorityCLI:
    method test_add_with_priority_option (line 3006) | def test_add_with_priority_option(self, extension_dir, project_dir):
    method test_list_shows_priority (line 3025) | def test_list_shows_priority(self, extension_dir, project_dir):
    method test_set_priority_changes_priority (line 3043) | def test_set_priority_changes_priority(self, extension_dir, project_dir):
    method test_set_priority_same_value_no_change (line 3068) | def test_set_priority_same_value_no_change(self, extension_dir, projec...
    method test_set_priority_invalid_value (line 3086) | def test_set_priority_invalid_value(self, extension_dir, project_dir):
    method test_set_priority_not_installed (line 3104) | def test_set_priority_not_installed(self, project_dir):
    method test_set_priority_by_display_name (line 3121) | def test_set_priority_by_display_name(self, extension_dir, project_dir):
  class TestExtensionPriorityBackwardsCompatibility (line 3145) | class TestExtensionPriorityBackwardsCompatibility:
    method test_legacy_extension_without_priority_field (line 3148) | def test_legacy_extension_without_priority_field(self, temp_dir):
    method test_legacy_extension_in_list_installed (line 3174) | def test_legacy_extension_in_list_installed(self, extension_dir, proje...
    method test_mixed_legacy_and_new_extensions_ordering (line 3191) | def test_mixed_legacy_and_new_extensions_ordering(self, temp_dir):

FILE: tests/test_merge.py
  function test_merge_json_files_type_mismatch_preservation (line 8) | def test_merge_json_files_type_mismatch_preservation(tmp_path):
  function test_merge_json_files_deep_nesting (line 23) | def test_merge_json_files_deep_nesting(tmp_path):
  function test_merge_json_files_empty_existing (line 50) | def test_merge_json_files_empty_existing(tmp_path):
  function test_merge_vscode_realistic_scenario (line 61) | def test_merge_vscode_realistic_scenario(tmp_path):
  function test_merge_json_files_with_bom (line 101) | def test_merge_json_files_with_bom(tmp_path):
  function test_merge_json_files_not_a_dictionary_template (line 112) | def test_merge_json_files_not_a_dictionary_template(tmp_path):
  function test_merge_json_files_unparseable_existing (line 120) | def test_merge_json_files_unparseable_existing(tmp_path):
  function test_merge_json_files_list_preservation (line 128) | def test_merge_json_files_list_preservation(tmp_path):
  function test_merge_json_files_no_changes (line 142) | def test_merge_json_files_no_changes(tmp_path):
  function test_merge_json_files_type_mismatch_no_op (line 155) | def test_merge_json_files_type_mismatch_no_op(tmp_path):
  function test_handle_vscode_settings_preserves_mode_on_atomic_write (line 168) | def test_handle_vscode_settings_preserves_mode_on_atomic_write(tmp_path):

FILE: tests/test_presets.py
  function temp_dir (line 42) | def temp_dir():
  function valid_pack_data (line 50) | def valid_pack_data():
  function pack_dir (line 82) | def pack_dir(temp_dir, valid_pack_data):
  function project_dir (line 104) | def project_dir(temp_dir):
  class TestPresetManifest (line 135) | class TestPresetManifest:
    method test_valid_manifest (line 138) | def test_valid_manifest(self, pack_dir):
    method test_missing_manifest (line 150) | def test_missing_manifest(self, temp_dir):
    method test_invalid_yaml (line 155) | def test_invalid_yaml(self, temp_dir):
    method test_missing_schema_version (line 162) | def test_missing_schema_version(self, temp_dir, valid_pack_data):
    method test_wrong_schema_version (line 171) | def test_wrong_schema_version(self, temp_dir, valid_pack_data):
    method test_missing_pack_id (line 180) | def test_missing_pack_id(self, temp_dir, valid_pack_data):
    method test_invalid_pack_id_format (line 189) | def test_invalid_pack_id_format(self, temp_dir, valid_pack_data):
    method test_invalid_version (line 198) | def test_invalid_version(self, temp_dir, valid_pack_data):
    method test_missing_speckit_version (line 207) | def test_missing_speckit_version(self, temp_dir, valid_pack_data):
    method test_no_templates_provided (line 216) | def test_no_templates_provided(self, temp_dir, valid_pack_data):
    method test_invalid_template_type (line 225) | def test_invalid_template_type(self, temp_dir, valid_pack_data):
    method test_valid_template_types (line 234) | def test_valid_template_types(self):
    method test_template_missing_required_fields (line 240) | def test_template_missing_required_fields(self, temp_dir, valid_pack_d...
    method test_invalid_template_name_format (line 249) | def test_invalid_template_name_format(self, temp_dir, valid_pack_data):
    method test_get_hash (line 258) | def test_get_hash(self, pack_dir):
    method test_multiple_templates (line 265) | def test_multiple_templates(self, temp_dir, valid_pack_data):
  class TestPresetRegistry (line 283) | class TestPresetRegistry:
    method test_empty_registry (line 286) | def test_empty_registry(self, temp_dir):
    method test_add_and_get (line 294) | def test_add_and_get(self, temp_dir):
    method test_remove (line 308) | def test_remove(self, temp_dir):
    method test_remove_nonexistent (line 320) | def test_remove_nonexistent(self, temp_dir):
    method test_list (line 327) | def test_list(self, temp_dir):
    method test_persistence (line 341) | def test_persistence(self, temp_dir):
    method test_corrupted_registry (line 354) | def test_corrupted_registry(self, temp_dir):
    method test_get_nonexistent (line 365) | def test_get_nonexistent(self, temp_dir):
    method test_restore (line 372) | def test_restore(self, temp_dir):
    method test_restore_rejects_none_metadata (line 393) | def test_restore_rejects_none_metadata(self, temp_dir):
    method test_restore_rejects_non_dict_metadata (line 402) | def test_restore_rejects_non_dict_metadata(self, temp_dir):
    method test_restore_uses_deep_copy (line 414) | def test_restore_uses_deep_copy(self, temp_dir):
    method test_get_returns_deep_copy (line 435) | def test_get_returns_deep_copy(self, temp_dir):
    method test_get_returns_none_for_corrupted_entry (line 453) | def test_get_returns_none_for_corrupted_entry(self, temp_dir):
    method test_list_returns_deep_copy (line 472) | def test_list_returns_deep_copy(self, temp_dir):
    method test_list_returns_empty_dict_for_corrupted_registry (line 490) | def test_list_returns_empty_dict_for_corrupted_registry(self, temp_dir):
    method test_list_by_priority_excludes_disabled (line 504) | def test_list_by_priority_excludes_disabled(self, temp_dir):
    method test_list_by_priority_includes_disabled_when_requested (line 521) | def test_list_by_priority_includes_disabled_when_requested(self, temp_...
  class TestPresetManager (line 542) | class TestPresetManager:
    method test_install_from_directory (line 545) | def test_install_from_directory(self, project_dir, pack_dir):
    method test_install_already_installed (line 559) | def test_install_already_installed(self, project_dir, pack_dir):
    method test_install_incompatible (line 567) | def test_install_incompatible(self, project_dir, temp_dir, valid_pack_...
    method test_install_from_zip (line 582) | def test_install_from_zip(self, project_dir, pack_dir, temp_dir):
    method test_install_from_zip_nested (line 596) | def test_install_from_zip_nested(self, project_dir, pack_dir, temp_dir):
    method test_install_from_zip_no_manifest (line 609) | def test_install_from_zip_no_manifest(self, project_dir, temp_dir):
    method test_remove (line 619) | def test_remove(self, project_dir, pack_dir):
    method test_remove_nonexistent (line 632) | def test_remove_nonexistent(self, project_dir):
    method test_list_installed (line 638) | def test_list_installed(self, project_dir, pack_dir):
    method test_list_installed_empty (line 650) | def test_list_installed_empty(self, project_dir):
    method test_get_pack (line 655) | def test_get_pack(self, project_dir, pack_dir):
    method test_get_pack_not_installed (line 664) | def test_get_pack_not_installed(self, project_dir):
    method test_check_compatibility_valid (line 669) | def test_check_compatibility_valid(self, pack_dir, temp_dir):
    method test_check_compatibility_invalid (line 675) | def test_check_compatibility_invalid(self, pack_dir, temp_dir):
    method test_install_with_priority (line 683) | def test_install_with_priority(self, project_dir, pack_dir):
    method test_install_default_priority (line 692) | def test_install_default_priority(self, project_dir, pack_dir):
    method test_list_installed_includes_priority (line 701) | def test_list_installed_includes_priority(self, project_dir, pack_dir):
  class TestRegistryPriority (line 711) | class TestRegistryPriority:
    method test_list_by_priority (line 714) | def test_list_by_priority(self, temp_dir):
    method test_list_by_priority_default (line 730) | def test_list_by_priority_default(self, temp_dir):
    method test_list_by_priority_invalid_priority_defaults (line 743) | def test_list_by_priority_invalid_priority_defaults(self, temp_dir):
  class TestPresetResolver (line 765) | class TestPresetResolver:
    method test_resolve_core_template (line 768) | def test_resolve_core_template(self, project_dir):
    method test_resolve_nonexistent (line 776) | def test_resolve_nonexistent(self, project_dir):
    method test_resolve_higher_priority_pack_wins (line 782) | def test_resolve_higher_priority_pack_wins(self, project_dir, temp_dir...
    method test_resolve_override_takes_priority (line 816) | def test_resolve_override_takes_priority(self, project_dir):
    method test_resolve_pack_takes_priority_over_core (line 829) | def test_resolve_pack_takes_priority_over_core(self, project_dir, pack...
    method test_resolve_override_takes_priority_over_pack (line 840) | def test_resolve_override_takes_priority_over_pack(self, project_dir, ...
    method test_resolve_extension_provided_templates (line 857) | def test_resolve_extension_provided_templates(self, project_dir):
    method test_resolve_disabled_extension_templates_skipped (line 876) | def test_resolve_disabled_extension_templates_skipped(self, project_dir):
    method test_resolve_disabled_extension_not_picked_up_as_unregistered (line 895) | def test_resolve_disabled_extension_not_picked_up_as_unregistered(self...
    method test_resolve_pack_over_extension (line 914) | def test_resolve_pack_over_extension(self, project_dir, pack_dir, temp...
    method test_resolve_with_source_core (line 933) | def test_resolve_with_source_core(self, project_dir):
    method test_resolve_with_source_override (line 941) | def test_resolve_with_source_override(self, project_dir):
    method test_resolve_with_source_pack (line 953) | def test_resolve_with_source_pack(self, project_dir, pack_dir):
    method test_resolve_with_source_extension (line 964) | def test_resolve_with_source_extension(self, project_dir):
    method test_resolve_with_source_not_found (line 982) | def test_resolve_with_source_not_found(self, project_dir):
    method test_resolve_skips_hidden_extension_dirs (line 988) | def test_resolve_skips_hidden_extension_dirs(self, project_dir):
  class TestExtensionPriorityResolution (line 1001) | class TestExtensionPriorityResolution:
    method test_unregistered_beats_registered_with_lower_precedence (line 1004) | def test_unregistered_beats_registered_with_lower_precedence(self, pro...
    method test_registered_with_higher_precedence_beats_unregistered (line 1028) | def test_registered_with_higher_precedence_beats_unregistered(self, pr...
    method test_unregistered_attribution_with_priority_ordering (line 1052) | def test_unregistered_attribution_with_priority_ordering(self, project...
    method test_same_priority_sorted_alphabetically (line 1077) | def test_same_priority_sorted_alphabetically(self, project_dir):
  class TestPresetCatalog (line 1102) | class TestPresetCatalog:
    method test_default_catalog_url (line 1105) | def test_default_catalog_url(self, project_dir):
    method test_community_catalog_url (line 1111) | def test_community_catalog_url(self, project_dir):
    method test_cache_validation_no_cache (line 1116) | def test_cache_validation_no_cache(self, project_dir):
    method test_cache_validation_valid (line 1121) | def test_cache_validation_valid(self, project_dir):
    method test_cache_validation_expired (line 1136) | def test_cache_validation_expired(self, project_dir):
    method test_cache_validation_corrupted (line 1151) | def test_cache_validation_corrupted(self, project_dir):
    method test_clear_cache (line 1161) | def test_clear_cache(self, project_dir):
    method test_search_with_cached_data (line 1173) | def test_search_with_cached_data(self, project_dir):
    method test_get_pack_info (line 1221) | def test_get_pack_info(self, project_dir):
    method test_validate_catalog_url_https (line 1248) | def test_validate_catalog_url_https(self, project_dir):
    method test_validate_catalog_url_http_rejected (line 1253) | def test_validate_catalog_url_http_rejected(self, project_dir):
    method test_validate_catalog_url_localhost_http_allowed (line 1259) | def test_validate_catalog_url_localhost_http_allowed(self, project_dir):
    method test_env_var_catalog_url (line 1265) | def test_env_var_catalog_url(self, project_dir, monkeypatch):
  class TestIntegration (line 1275) | class TestIntegration:
    method test_full_install_resolve_remove_cycle (line 1278) | def test_full_install_resolve_remove_cycle(self, project_dir, pack_dir):
    method test_override_beats_pack_beats_extension_beats_core (line 1299) | def test_override_beats_pack_beats_extension_beats_core(self, project_...
    method test_install_from_zip_then_resolve (line 1336) | def test_install_from_zip_then_resolve(self, project_dir, pack_dir, te...
  class TestPresetCatalogEntry (line 1360) | class TestPresetCatalogEntry:
    method test_create_entry (line 1363) | def test_create_entry(self):
    method test_default_description (line 1378) | def test_default_description(self):
  class TestPresetCatalogMultiCatalog (line 1392) | class TestPresetCatalogMultiCatalog:
    method test_default_active_catalogs (line 1395) | def test_default_active_catalogs(self, project_dir):
    method test_env_var_overrides_catalogs (line 1407) | def test_env_var_overrides_catalogs(self, project_dir, monkeypatch):
    method test_project_config_overrides_defaults (line 1420) | def test_project_config_overrides_defaults(self, project_dir):
    method test_load_catalog_config_nonexistent (line 1440) | def test_load_catalog_config_nonexistent(self, project_dir):
    method test_load_catalog_config_empty (line 1448) | def test_load_catalog_config_empty(self, project_dir):
    method test_load_catalog_config_invalid_yaml (line 1457) | def test_load_catalog_config_invalid_yaml(self, project_dir):
    method test_load_catalog_config_not_a_list (line 1466) | def test_load_catalog_config_not_a_list(self, project_dir):
    method test_load_catalog_config_invalid_entry (line 1475) | def test_load_catalog_config_invalid_entry(self, project_dir):
    method test_load_catalog_config_http_url_rejected (line 1484) | def test_load_catalog_config_http_url_rejected(self, project_dir):
    method test_load_catalog_config_priority_sorting (line 1501) | def test_load_catalog_config_priority_sorting(self, project_dir):
    method test_load_catalog_config_invalid_priority (line 1528) | def test_load_catalog_config_invalid_priority(self, project_dir):
    method test_load_catalog_config_install_allowed_string (line 1545) | def test_load_catalog_config_install_allowed_string(self, project_dir):
    method test_get_catalog_url_uses_highest_priority (line 1564) | def test_get_catalog_url_uses_highest_priority(self, project_dir):
    method test_cache_paths_default_url (line 1585) | def test_cache_paths_default_url(self, project_dir):
    method test_cache_paths_custom_url (line 1594) | def test_cache_paths_custom_url(self, project_dir):
    method test_url_cache_valid (line 1604) | def test_url_cache_valid(self, project_dir):
    method test_url_cache_expired (line 1618) | def test_url_cache_expired(self, project_dir):
  class TestSelfTestPreset (line 1648) | class TestSelfTestPreset:
    method test_self_test_preset_exists (line 1651) | def test_self_test_preset_exists(self):
    method test_self_test_manifest_valid (line 1656) | def test_self_test_manifest_valid(self):
    method test_self_test_provides_all_core_templates (line 1664) | def test_self_test_provides_all_core_templates(self):
    method test_self_test_template_files_exist (line 1671) | def test_self_test_template_files_exist(self):
    method test_self_test_templates_have_marker (line 1678) | def test_self_test_templates_have_marker(self):
    method test_install_self_test_preset (line 1685) | def test_install_self_test_preset(self, project_dir):
    method test_self_test_overrides_all_core_templates (line 1692) | def test_self_test_overrides_all_core_templates(self, project_dir):
    method test_self_test_resolve_with_source (line 1713) | def test_self_test_resolve_with_source(self, project_dir):
    method test_self_test_removal_restores_core (line 1730) | def test_self_test_removal_restores_core(self, project_dir):
    method test_self_test_not_in_catalog (line 1746) | def test_self_test_not_in_catalog(self):
    method test_self_test_has_command (line 1752) | def test_self_test_has_command(self):
    method test_self_test_command_file_exists (line 1759) | def test_self_test_command_file_exists(self):
    method test_self_test_registers_commands_for_claude (line 1766) | def test_self_test_registers_commands_for_claude(self, project_dir):
    method test_self_test_registers_commands_for_gemini (line 1781) | def test_self_test_registers_commands_for_gemini(self, project_dir):
    method test_self_test_unregisters_commands_on_remove (line 1797) | def test_self_test_unregisters_commands_on_remove(self, project_dir):
    method test_self_test_no_commands_without_agent_dirs (line 1811) | def test_self_test_no_commands_without_agent_dirs(self, project_dir):
    method test_extension_command_skipped_when_extension_missing (line 1819) | def test_extension_command_skipped_when_extension_missing(self, projec...
    method test_extension_command_registered_when_extension_present (line 1862) | def test_extension_command_registered_when_extension_present(self, pro...
  class TestInitOptions (line 1907) | class TestInitOptions:
    method test_save_and_load_round_trip (line 1910) | def test_save_and_load_round_trip(self, project_dir):
    method test_load_returns_empty_when_missing (line 1920) | def test_load_returns_empty_when_missing(self, project_dir):
    method test_load_returns_empty_on_invalid_json (line 1925) | def test_load_returns_empty_on_invalid_json(self, project_dir):
  class TestPresetSkills (line 1935) | class TestPresetSkills:
    method _write_init_options (line 1938) | def _write_init_options(self, project_dir, ai="claude", ai_skills=True):
    method _create_skill (line 1943) | def _create_skill(self, skills_dir, skill_name, body="original body"):
    method test_skill_overridden_on_preset_install (line 1951) | def test_skill_overridden_on_preset_install(self, project_dir, temp_dir):
    method test_skill_not_updated_when_ai_skills_disabled (line 1975) | def test_skill_not_updated_when_ai_skills_disabled(self, project_dir, ...
    method test_skill_not_updated_without_init_options (line 1991) | def test_skill_not_updated_without_init_options(self, project_dir, tem...
    method test_skill_restored_on_preset_remove (line 2006) | def test_skill_restored_on_preset_remove(self, project_dir, temp_dir):
    method test_no_skills_registered_when_no_skill_dir_exists (line 2036) | def test_no_skills_registered_when_no_skill_dir_exists(self, project_d...
  class TestPresetSetPriority (line 2051) | class TestPresetSetPriority:
    method test_set_priority_changes_priority (line 2054) | def test_set_priority_changes_priority(self, project_dir, pack_dir):
    method test_set_priority_same_value_no_change (line 2079) | def test_set_priority_same_value_no_change(self, project_dir, pack_dir):
    method test_set_priority_invalid_value (line 2097) | def test_set_priority_invalid_value(self, project_dir, pack_dir):
    method test_set_priority_not_installed (line 2115) | def test_set_priority_not_installed(self, project_dir):
  class TestPresetPriorityBackwardsCompatibility (line 2130) | class TestPresetPriorityBackwardsCompatibility:
    method test_legacy_preset_without_priority_field (line 2133) | def test_legacy_preset_without_priority_field(self, temp_dir):
    method test_legacy_preset_in_list_installed (line 2159) | def test_legacy_preset_in_list_installed(self, project_dir, pack_dir):
    method test_mixed_legacy_and_new_presets_ordering (line 2176) | def test_mixed_legacy_and_new_presets_ordering(self, temp_dir):
  class TestPresetEnableDisable (line 2210) | class TestPresetEnableDisable:
    method test_disable_preset (line 2213) | def test_disable_preset(self, project_dir, pack_dir):
    method test_enable_preset (line 2238) | def test_enable_preset(self, project_dir, pack_dir):
    method test_disable_already_disabled (line 2264) | def test_disable_already_disabled(self, project_dir, pack_dir):
    method test_enable_already_enabled (line 2283) | def test_enable_already_enabled(self, project_dir, pack_dir):
    method test_disable_not_installed (line 2301) | def test_disable_not_installed(self, project_dir):
    method test_enable_not_installed (line 2315) | def test_enable_not_installed(self, project_dir):
    method test_disabled_preset_excluded_from_resolution (line 2329) | def test_disabled_preset_excluded_from_resolution(self, project_dir, p...
    method test_enable_corrupted_registry_entry (line 2355) | def test_enable_corrupted_registry_entry(self, project_dir, pack_dir):
    method test_disable_corrupted_registry_entry (line 2375) | def test_disable_corrupted_registry_entry(self, project_dir, pack_dir):
Condensed preview — 130 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,414K chars).
[
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 2205,
    "preview": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the\n// README at: https://github.co"
  },
  {
    "path": ".devcontainer/post-create.sh",
    "chars": 3193,
    "preview": "#!/bin/bash\n\n# Exit immediately on error, treat unset variables as an error, and fail if any command in a pipeline fails"
  },
  {
    "path": ".gitattributes",
    "chars": 19,
    "preview": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 31,
    "preview": "# Global code owner\n* @mnriem\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/agent_request.yml",
    "chars": 4480,
    "preview": "name: Agent Request\ndescription: Request support for a new AI agent/assistant in Spec Kit\ntitle: \"[Agent]: Add support f"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2910,
    "preview": "name: Bug Report\ndescription: Report a bug or unexpected behavior in Specify CLI or Spec Kit\ntitle: \"[Bug]: \"\nlabels: [\""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 844,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: 💬 General Discussion\n    url: https://github.com/github/spec-kit/di"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/extension_submission.yml",
    "chars": 8511,
    "preview": "name: Extension Submission\ndescription: Submit your extension to the Spec Kit catalog\ntitle: \"[Extension]: Add \"\nlabels:"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 2905,
    "preview": "name: Feature Request\ndescription: Suggest a new feature or enhancement for Specify CLI or Spec Kit\ntitle: \"[Feature]: \""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/preset_submission.yml",
    "chars": 5222,
    "preview": "name: Preset Submission\ndescription: Submit your preset to the Spec Kit preset catalog\ntitle: \"[Preset]: Add \"\nlabels: ["
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 706,
    "preview": "## Description\n\n<!-- What does this PR do? Why is it needed? -->\n\n## Testing\n\n<!-- How did you test your changes? -->\n\n-"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 206,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"pip\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n\n  - package-"
  },
  {
    "path": ".github/workflows/RELEASE-PROCESS.md",
    "chars": 6399,
    "preview": "# Release Process\n\nThis document describes the automated release process for Spec Kit.\n\n## Overview\n\nThe release process"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "chars": 680,
    "preview": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  analyze:\n    name: An"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1633,
    "preview": "# Build and deploy DocFX documentation to GitHub Pages\nname: Deploy Documentation to Pages\n\non:\n  # Runs on pushes targe"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 389,
    "preview": "name: Lint\npermissions:\n  contents: read\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n\njobs:\n  markdownlint:\n    "
  },
  {
    "path": ".github/workflows/release-trigger.yml",
    "chars": 6057,
    "preview": "name: Release Trigger\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version to release (e.g"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 2120,
    "preview": "name: Create Release\n\non:\n  push:\n    tags:\n      - 'v*'\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    permissions:\n "
  },
  {
    "path": ".github/workflows/scripts/check-release-exists.sh",
    "chars": 507,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# check-release-exists.sh\n# Check if a GitHub release already exists for the give"
  },
  {
    "path": ".github/workflows/scripts/create-github-release.sh",
    "chars": 3545,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# create-github-release.sh\n# Create a GitHub release with all template zip files\n"
  },
  {
    "path": ".github/workflows/scripts/create-release-packages.ps1",
    "chars": 21344,
    "preview": "#!/usr/bin/env pwsh\n#requires -Version 7.0\n\n<#\n.SYNOPSIS\n    Build Spec Kit template release archives for each supported"
  },
  {
    "path": ".github/workflows/scripts/create-release-packages.sh",
    "chars": 14916,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# create-release-packages.sh (workflow-local)\n# Build Spec Kit template release a"
  },
  {
    "path": ".github/workflows/scripts/generate-release-notes.sh",
    "chars": 1141,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# generate-release-notes.sh\n# Generate release notes from git history\n# Usage: ge"
  },
  {
    "path": ".github/workflows/scripts/get-next-version.sh",
    "chars": 750,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# get-next-version.sh\n# Calculate the next version based on the latest git tag an"
  },
  {
    "path": ".github/workflows/scripts/simulate-release.sh",
    "chars": 5218,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# simulate-release.sh\n# Simulate the release process locally without pushing to G"
  },
  {
    "path": ".github/workflows/scripts/update-version.sh",
    "chars": 589,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\n\n# update-version.sh\n# Update version in pyproject.toml (for release artifacts onl"
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 1911,
    "preview": "name: 'Close stale issues and PRs'\n\non:\n  schedule:\n    - cron: '0 0 * * *' # Run daily at midnight UTC\n  workflow_dispa"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 989,
    "preview": "name: Test & Lint Python\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n\njobs:\n  ruf"
  },
  {
    "path": ".gitignore",
    "chars": 504,
    "preview": "# Python\n__pycache__/\n*.py[cod]\n*$py.class\n*.so\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\np"
  },
  {
    "path": ".markdownlint-cli2.jsonc",
    "chars": 481,
    "preview": "{\n  // https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md\n  \"config\": {\n    \"default\": true,\n    \"MD003\":"
  },
  {
    "path": "AGENTS.md",
    "chars": 16480,
    "preview": "# AGENTS.md\n\n## About Spec Kit and Specify\n\n**GitHub Spec Kit** is a comprehensive toolkit for implementing Spec-Driven "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 16992,
    "preview": "# Changelog\n\n## [0.3.2] - 2026-03-19\n\n### Changes\n\n- Add conduct extension to community catalog (#1908)\n- feat(extension"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3233,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7782,
    "preview": "# Contributing to Spec Kit\n\nHi there! We're thrilled that you'd like to contribute to Spec Kit. Contributions to this pr"
  },
  {
    "path": "LICENSE",
    "chars": 1061,
    "preview": "MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof thi"
  },
  {
    "path": "README.md",
    "chars": 50323,
    "preview": "<div align=\"center\">\n    <img src=\"./media/logo_large.webp\" alt=\"Spec Kit Logo\" width=\"200\" height=\"200\"/>\n    <h1>🌱 Spe"
  },
  {
    "path": "SECURITY.md",
    "chars": 1736,
    "preview": "# Security Policy\n\nThanks for helping make GitHub safe for everyone.\n\nGitHub takes the security of our software products"
  },
  {
    "path": "SUPPORT.md",
    "chars": 1001,
    "preview": "# Support\n\n## How to get help\n\nPlease search existing [issues](https://github.com/github/spec-kit/issues) and [discussio"
  },
  {
    "path": "docs/.gitignore",
    "chars": 73,
    "preview": "# DocFX build output\n_site/\nobj/\n.docfx/\n\n# Temporary files\n*.tmp\n*.log\n\n"
  },
  {
    "path": "docs/README.md",
    "chars": 899,
    "preview": "# Documentation\n\nThis folder contains the documentation source files for Spec Kit, built using [DocFX](https://dotnet.gi"
  },
  {
    "path": "docs/docfx.json",
    "chars": 1383,
    "preview": "{\n  \"build\": {\n    \"content\": [\n      {\n        \"files\": [\n          \"*.md\",\n          \"toc.yml\"\n        ]\n      },\n    "
  },
  {
    "path": "docs/index.md",
    "chars": 3145,
    "preview": "# Spec Kit\n\n*Build high-quality software faster.*\n\n**An effort to allow organizations to focus on product scenarios rath"
  },
  {
    "path": "docs/installation.md",
    "chars": 3172,
    "preview": "# Installation Guide\n\n## Prerequisites\n\n- **Linux/macOS** (or Windows; PowerShell scripts now supported without WSL)\n- A"
  },
  {
    "path": "docs/local-development.md",
    "chars": 4862,
    "preview": "# Local Development Guide\n\nThis guide shows how to iterate on the `specify` CLI locally without publishing a release or "
  },
  {
    "path": "docs/quickstart.md",
    "chars": 7780,
    "preview": "# Quick Start Guide\n\nThis guide will help you get started with Spec-Driven Development using Spec Kit.\n\n> [!NOTE]\n> All "
  },
  {
    "path": "docs/toc.yml",
    "chars": 365,
    "preview": "# Home page\n- name: Home\n  href: index.md\n\n# Getting started section\n- name: Getting Started\n  items:\n    - name: Instal"
  },
  {
    "path": "docs/upgrade.md",
    "chars": 13254,
    "preview": "# Upgrade Guide\n\n> You have Spec Kit installed and want to upgrade to the latest version to get new features, bug fixes,"
  },
  {
    "path": "extensions/EXTENSION-API-REFERENCE.md",
    "chars": 18993,
    "preview": "# Extension API Reference\n\nTechnical reference for Spec Kit extension system APIs and manifest schema.\n\n## Table of Cont"
  },
  {
    "path": "extensions/EXTENSION-DEVELOPMENT-GUIDE.md",
    "chars": 16565,
    "preview": "# Extension Development Guide\n\nA guide for creating Spec Kit extensions.\n\n---\n\n## Quick Start\n\n### 1. Create Extension D"
  },
  {
    "path": "extensions/EXTENSION-PUBLISHING-GUIDE.md",
    "chars": 14822,
    "preview": "# Extension Publishing Guide\n\nThis guide explains how to publish your extension to the Spec Kit extension catalog, makin"
  },
  {
    "path": "extensions/EXTENSION-USER-GUIDE.md",
    "chars": 23774,
    "preview": "# Extension User Guide\n\nComplete guide for using Spec Kit extensions to enhance your workflow.\n\n## Table of Contents\n\n1."
  },
  {
    "path": "extensions/README.md",
    "chars": 10231,
    "preview": "# Spec Kit Extensions\n\nExtension system for [Spec Kit](https://github.com/github/spec-kit) - add new functionality witho"
  },
  {
    "path": "extensions/RFC-EXTENSION-SYSTEM.md",
    "chars": 58583,
    "preview": "# RFC: Spec Kit Extension System\n\n**Status**: Implemented\n**Author**: Stats Perform Engineering\n**Created**: 2026-01-28\n"
  },
  {
    "path": "extensions/catalog.community.json",
    "chars": 26553,
    "preview": "{\n  \"schema_version\": \"1.0\",\n  \"updated_at\": \"2026-03-19T12:08:20Z\",\n  \"catalog_url\": \"https://raw.githubusercontent.com"
  },
  {
    "path": "extensions/catalog.json",
    "chars": 729,
    "preview": "{\n  \"schema_version\": \"1.0\",\n  \"updated_at\": \"2026-03-10T00:00:00Z\",\n  \"catalog_url\": \"https://raw.githubusercontent.com"
  },
  {
    "path": "extensions/selftest/commands/selftest.md",
    "chars": 2565,
    "preview": "---\ndescription: \"Validate the lifecycle of an extension from the catalog.\"\n---\n\n# Extension Self-Test: `$ARGUMENTS`\n\nTh"
  },
  {
    "path": "extensions/selftest/extension.yml",
    "chars": 533,
    "preview": "schema_version: \"1.0\"\nextension:\n  id: selftest\n  name: Spec Kit Self-Test Utility\n  version: 1.0.0\n  description: Verif"
  },
  {
    "path": "extensions/template/.gitignore",
    "chars": 320,
    "preview": "# Local configuration overrides\n*-config.local.yml\n\n# Python\n__pycache__/\n*.py[cod]\n*$py.class\n*.so\n.Python\nenv/\nvenv/\n\n"
  },
  {
    "path": "extensions/template/CHANGELOG.md",
    "chars": 888,
    "preview": "# Changelog\n\nAll notable changes to this extension will be documented in this file.\n\nThe format is based on [Keep a Chan"
  },
  {
    "path": "extensions/template/EXAMPLE-README.md",
    "chars": 3144,
    "preview": "# EXAMPLE: Extension README\n\nThis is an example of what your extension README should look like after customization.\n**De"
  },
  {
    "path": "extensions/template/LICENSE",
    "chars": 1084,
    "preview": "MIT License\n\nCopyright (c) 2026 [Your Name or Organization]\n\nPermission is hereby granted, free of charge, to any person"
  },
  {
    "path": "extensions/template/README.md",
    "chars": 2067,
    "preview": "# Extension Template\n\nStarter template for creating a Spec Kit extension.\n\n## Quick Start\n\n1. **Copy this template**:\n\n "
  },
  {
    "path": "extensions/template/commands/example.md",
    "chars": 4428,
    "preview": "---\ndescription: \"Example command that demonstrates extension functionality\"\n# CUSTOMIZE: List MCP tools this command us"
  },
  {
    "path": "extensions/template/config-template.yml",
    "chars": 1872,
    "preview": "# Extension Configuration Template\n# Copy this to my-extension-config.yml and customize for your project\n\n# CUSTOMIZE: A"
  },
  {
    "path": "extensions/template/extension.yml",
    "chars": 3018,
    "preview": "schema_version: \"1.0\"\n\nextension:\n  # CUSTOMIZE: Change 'my-extension' to your extension ID (lowercase, hyphen-separated"
  },
  {
    "path": "newsletters/2026-February.md",
    "chars": 11584,
    "preview": "# Spec Kit - February 2026 Newsletter\n\nThis edition covers Spec Kit activity in February 2026. Versions v0.1.7 through v"
  },
  {
    "path": "presets/ARCHITECTURE.md",
    "chars": 6762,
    "preview": "# Preset System Architecture\n\nThis document describes the internal architecture of the preset system — how template reso"
  },
  {
    "path": "presets/PUBLISHING.md",
    "chars": 8248,
    "preview": "# Preset Publishing Guide\n\nThis guide explains how to publish your preset to the Spec Kit preset catalog, making it disc"
  },
  {
    "path": "presets/README.md",
    "chars": 5149,
    "preview": "# Presets\n\nPresets are stackable, priority-ordered collections of template and command overrides for Spec Kit. They let "
  },
  {
    "path": "presets/catalog.community.json",
    "chars": 193,
    "preview": "{\n  \"schema_version\": \"1.0\",\n  \"updated_at\": \"2026-03-09T00:00:00Z\",\n  \"catalog_url\": \"https://raw.githubusercontent.com"
  },
  {
    "path": "presets/catalog.json",
    "chars": 183,
    "preview": "{\n  \"schema_version\": \"1.0\",\n  \"updated_at\": \"2026-03-10T00:00:00Z\",\n  \"catalog_url\": \"https://raw.githubusercontent.com"
  },
  {
    "path": "presets/scaffold/README.md",
    "chars": 1728,
    "preview": "# My Preset\n\nA custom preset for Spec Kit. Copy this directory and customize it to create your own.\n\n## Templates Includ"
  },
  {
    "path": "presets/scaffold/commands/speckit.myext.myextcmd.md",
    "chars": 753,
    "preview": "---\ndescription: \"Override of the myext extension's myextcmd command\"\n---\n\n<!-- Preset override for speckit.myext.myextc"
  },
  {
    "path": "presets/scaffold/commands/speckit.specify.md",
    "chars": 707,
    "preview": "---\ndescription: \"Create a feature specification (preset override)\"\nscripts:\n  sh: scripts/bash/create-new-feature.sh \"{"
  },
  {
    "path": "presets/scaffold/preset.yml",
    "chars": 3145,
    "preview": "schema_version: \"1.0\"\n\npreset:\n  # CUSTOMIZE: Change 'my-preset' to your preset ID (lowercase, hyphen-separated)\n  id: \""
  },
  {
    "path": "presets/scaffold/templates/myext-template.md",
    "chars": 422,
    "preview": "# MyExt Report\n\n> This template overrides the one provided by the \"myext\" extension.\n> Customize it to match your needs."
  },
  {
    "path": "presets/scaffold/templates/spec-template.md",
    "chars": 247,
    "preview": "# Feature Specification: [FEATURE NAME]\n\n**Created**: [DATE]\n**Status**: Draft\n\n## Overview\n\n[Brief description of the f"
  },
  {
    "path": "presets/self-test/commands/speckit.specify.md",
    "chars": 439,
    "preview": "---\ndescription: \"Self-test override of the specify command\"\n---\n\n<!-- preset:self-test -->\n\nYou are following the self-"
  },
  {
    "path": "presets/self-test/preset.yml",
    "chars": 1655,
    "preview": "schema_version: \"1.0\"\n\npreset:\n  id: \"self-test\"\n  name: \"Self-Test Preset\"\n  version: \"1.0.0\"\n  description: \"A preset "
  },
  {
    "path": "presets/self-test/templates/agent-file-template.md",
    "chars": 191,
    "preview": "# Agent File (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test preset.\n\n## Age"
  },
  {
    "path": "presets/self-test/templates/checklist-template.md",
    "chars": 249,
    "preview": "# Checklist (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test preset.\n\n## Pre-"
  },
  {
    "path": "presets/self-test/templates/constitution-template.md",
    "chars": 205,
    "preview": "# Constitution (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test preset.\n\n## P"
  },
  {
    "path": "presets/self-test/templates/plan-template.md",
    "chars": 258,
    "preview": "# Implementation Plan (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test preset"
  },
  {
    "path": "presets/self-test/templates/spec-template.md",
    "chars": 325,
    "preview": "# Feature Specification (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test pres"
  },
  {
    "path": "presets/self-test/templates/tasks-template.md",
    "chars": 239,
    "preview": "# Tasks (Self-Test Preset)\n\n<!-- preset:self-test -->\n\n> This template is provided by the self-test preset.\n\n## Task Lis"
  },
  {
    "path": "pyproject.toml",
    "chars": 1063,
    "preview": "[project]\nname = \"specify-cli\"\nversion = \"0.3.2\"\ndescription = \"Specify CLI, part of GitHub Spec Kit. A tool to bootstra"
  },
  {
    "path": "scripts/bash/check-prerequisites.sh",
    "chars": 6154,
    "preview": "#!/usr/bin/env bash\n\n# Consolidated prerequisite checking script\n#\n# This script provides unified prerequisite checking "
  },
  {
    "path": "scripts/bash/common.sh",
    "chars": 9947,
    "preview": "#!/usr/bin/env bash\n# Common functions and variables for all scripts\n\n# Get repository root, with fallback for non-git r"
  },
  {
    "path": "scripts/bash/create-new-feature.sh",
    "chars": 11496,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\nJSON_MODE=false\nSHORT_NAME=\"\"\nBRANCH_NUMBER=\"\"\nARGS=()\ni=1\nwhile [ $i -le $# ]; do\n    arg="
  },
  {
    "path": "scripts/bash/setup-plan.sh",
    "chars": 2247,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\n# Parse command line arguments\nJSON_MODE=false\nARGS=()\n\nfor arg in \"$@\"; do\n    case \"$arg\""
  },
  {
    "path": "scripts/bash/update-agent-context.sh",
    "chars": 29641,
    "preview": "#!/usr/bin/env bash\n\n# Update agent context files with information from plan.md\n#\n# This script maintains AI agent conte"
  },
  {
    "path": "scripts/powershell/check-prerequisites.ps1",
    "chars": 4805,
    "preview": "#!/usr/bin/env pwsh\n\n# Consolidated prerequisite checking script (PowerShell)\n#\n# This script provides unified prerequis"
  },
  {
    "path": "scripts/powershell/common.ps1",
    "chars": 6574,
    "preview": "#!/usr/bin/env pwsh\n# Common PowerShell functions analogous to common.sh\n\nfunction Get-RepoRoot {\n    try {\n        $res"
  },
  {
    "path": "scripts/powershell/create-new-feature.ps1",
    "chars": 10485,
    "preview": "#!/usr/bin/env pwsh\n# Create a new feature\n[CmdletBinding()]\nparam(\n    [switch]$Json,\n    [string]$ShortName,\n    [Para"
  },
  {
    "path": "scripts/powershell/setup-plan.ps1",
    "chars": 1866,
    "preview": "#!/usr/bin/env pwsh\n# Setup implementation plan for a feature\n\n[CmdletBinding()]\nparam(\n    [switch]$Json,\n    [switch]$"
  },
  {
    "path": "scripts/powershell/update-agent-context.ps1",
    "chars": 23396,
    "preview": "#!/usr/bin/env pwsh\n<#!\n.SYNOPSIS\nUpdate agent context files with information from plan.md (PowerShell version)\n\n.DESCRI"
  },
  {
    "path": "spec-driven.md",
    "chars": 25260,
    "preview": "# Specification-Driven Development (SDD)\n\n## The Power Inversion\n\nFor decades, code has been king. Specifications served"
  },
  {
    "path": "spec-kit.code-workspace",
    "chars": 60,
    "preview": "{\n\t\"folders\": [\n\t\t{\n\t\t\t\"path\": \".\"\n\t\t}\n\t],\n\t\"settings\": {}\n}"
  },
  {
    "path": "src/specify_cli/__init__.py",
    "chars": 174650,
    "preview": "#!/usr/bin/env python3\n# /// script\n# requires-python = \">=3.11\"\n# dependencies = [\n#     \"typer\",\n#     \"rich\",\n#     \""
  },
  {
    "path": "src/specify_cli/agents.py",
    "chars": 19376,
    "preview": "\"\"\"\nAgent Command Registrar for Spec Kit\n\nShared infrastructure for registering commands with AI agents.\nUsed by both th"
  },
  {
    "path": "src/specify_cli/extensions.py",
    "chars": 73537,
    "preview": "\"\"\"\nExtension Manager for Spec Kit\n\nHandles installation, removal, and management of Spec Kit extensions.\nExtensions are"
  },
  {
    "path": "src/specify_cli/presets.py",
    "chars": 62566,
    "preview": "\"\"\"\nPreset Manager for Spec Kit\n\nHandles installation, removal, and management of Spec Kit presets.\nPresets are self-con"
  },
  {
    "path": "templates/agent-file-template.md",
    "chars": 464,
    "preview": "# [PROJECT NAME] Development Guidelines\n\nAuto-generated from all feature plans. Last updated: [DATE]\n\n## Active Technolo"
  },
  {
    "path": "templates/checklist-template.md",
    "chars": 1312,
    "preview": "# [CHECKLIST TYPE] Checklist: [FEATURE NAME]\n\n**Purpose**: [Brief description of what this checklist covers]\n**Created**"
  },
  {
    "path": "templates/commands/analyze.md",
    "chars": 7249,
    "preview": "---\ndescription: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and "
  },
  {
    "path": "templates/commands/checklist.md",
    "chars": 16987,
    "preview": "---\ndescription: Generate a custom checklist for the current feature based on user requirements.\nscripts:\n  sh: scripts/"
  },
  {
    "path": "templates/commands/clarify.md",
    "chars": 11387,
    "preview": "---\ndescription: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarificati"
  },
  {
    "path": "templates/commands/constitution.md",
    "chars": 5437,
    "preview": "---\ndescription: Create or update the project constitution from interactive or provided principle inputs, ensuring all d"
  },
  {
    "path": "templates/commands/implement.md",
    "chars": 10460,
    "preview": "---\ndescription: Execute the implementation plan by processing and executing all tasks defined in tasks.md\nscripts:\n  sh"
  },
  {
    "path": "templates/commands/plan.md",
    "chars": 6325,
    "preview": "---\ndescription: Execute the implementation planning workflow using the plan template to generate design artifacts.\nhand"
  },
  {
    "path": "templates/commands/specify.md",
    "chars": 14884,
    "preview": "---\ndescription: Create or update the feature specification from a natural language feature description.\nhandoffs: \n  - "
  },
  {
    "path": "templates/commands/tasks.md",
    "chars": 9195,
    "preview": "---\ndescription: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts"
  },
  {
    "path": "templates/commands/taskstoissues.md",
    "chars": 1187,
    "preview": "---\ndescription: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on avail"
  },
  {
    "path": "templates/constitution-template.md",
    "chars": 2336,
    "preview": "# [PROJECT_NAME] Constitution\n<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->\n\n## Core Principles\n\n### "
  },
  {
    "path": "templates/plan-template.md",
    "chars": 3546,
    "preview": "# Implementation Plan: [FEATURE]\n\n**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]\n**Input**: Feat"
  },
  {
    "path": "templates/spec-template.md",
    "chars": 3960,
    "preview": "# Feature Specification: [FEATURE NAME]\n\n**Feature Branch**: `[###-feature-name]`  \n**Created**: [DATE]  \n**Status**: Dr"
  },
  {
    "path": "templates/tasks-template.md",
    "chars": 9140,
    "preview": "---\n\ndescription: \"Task list template for feature implementation\"\n---\n\n# Tasks: [FEATURE NAME]\n\n**Input**: Design docume"
  },
  {
    "path": "templates/vscode-settings.json",
    "chars": 351,
    "preview": "{\n    \"chat.promptFilesRecommendations\": {\n        \"speckit.constitution\": true,\n        \"speckit.specify\": true,\n      "
  },
  {
    "path": "tests/__init__.py",
    "chars": 31,
    "preview": "\"\"\"Unit tests for Spec Kit.\"\"\"\n"
  },
  {
    "path": "tests/hooks/.specify/extensions.yml",
    "chars": 960,
    "preview": "hooks:\n  before_implement:\n    - id: pre_test\n      enabled: true\n      optional: false\n      extension: \"test-extension"
  },
  {
    "path": "tests/hooks/TESTING.md",
    "chars": 1832,
    "preview": "# Testing Extension Hooks\n\nThis directory contains a mock project to verify that LLM agents correctly identify and execu"
  },
  {
    "path": "tests/hooks/plan.md",
    "chars": 153,
    "preview": "# Test Setup for Hooks\n\nThis feature is designed to test if LLMs correctly invoke Spec Kit extensions hooks when generat"
  },
  {
    "path": "tests/hooks/spec.md",
    "chars": 69,
    "preview": "- **User Story 1:** I want a test script that prints \"Hello hooks!\".\n"
  },
  {
    "path": "tests/hooks/tasks.md",
    "chars": 70,
    "preview": "- [ ] T001 [US1] Create script that prints 'Hello hooks!' in hello.py\n"
  },
  {
    "path": "tests/test_agent_config_consistency.py",
    "chars": 23529,
    "preview": "\"\"\"Consistency checks for agent configuration across runtime and packaging scripts.\"\"\"\n\nimport re\nfrom pathlib import Pa"
  },
  {
    "path": "tests/test_ai_skills.py",
    "chars": 48894,
    "preview": "\"\"\"\nUnit tests for AI agent skills installation.\n\nTests cover:\n- Skills directory resolution for different agents (_get_"
  },
  {
    "path": "tests/test_cursor_frontmatter.py",
    "chars": 9256,
    "preview": "\"\"\"\nTests for Cursor .mdc frontmatter generation (issue #669).\n\nVerifies that update-agent-context.sh properly prepends "
  },
  {
    "path": "tests/test_extensions.py",
    "chars": 119251,
    "preview": "\"\"\"\nUnit tests for the extension system.\n\nTests cover:\n- Extension manifest validation\n- Extension registry operations\n-"
  },
  {
    "path": "tests/test_merge.py",
    "chars": 6617,
    "preview": "import stat\n\nfrom specify_cli import merge_json_files\nfrom specify_cli import handle_vscode_settings\n\n# --- Dimension 2:"
  },
  {
    "path": "tests/test_presets.py",
    "chars": 97728,
    "preview": "\"\"\"\nUnit tests for the preset system.\n\nTests cover:\n- Preset manifest validation\n- Preset registry operations\n- Preset m"
  }
]

About this extraction

This page contains the full source code of the github/spec-kit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 130 files (1.3 MB), approximately 309.6k tokens, and a symbol index with 714 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!