Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
41c06ed3b2 chore: bump version to 0.8.5 2026-05-04 16:37:48 +00:00
43 changed files with 328 additions and 980 deletions

5
.github/CODEOWNERS vendored
View File

@@ -1,8 +1,3 @@
# Global code owner
* @mnriem
# Community catalog files — explicit ownership for when global ownership expands
/extensions/catalog.community.json @mnriem
/integrations/catalog.community.json @mnriem
/presets/catalog.community.json @mnriem

View File

@@ -95,18 +95,11 @@ body:
validations:
required: true
- type: input
id: required-extensions
attributes:
label: Required Extensions (optional)
description: Comma-separated list of required extension IDs (e.g., aide)
placeholder: "e.g., aide, canon"
- type: textarea
id: templates-provided
attributes:
label: Templates Provided
description: List the template overrides your preset provides (enter "None" if command-only)
description: List the template overrides your preset provides
placeholder: |
- spec-template.md — adds compliance section
- plan-template.md — includes audit checkpoints
@@ -117,19 +110,10 @@ body:
- type: textarea
id: commands-provided
attributes:
label: Commands Provided
description: List the command overrides your preset provides (enter "None" if template-only)
label: Commands Provided (optional)
description: List any command overrides your preset provides
placeholder: |
- speckit.specify.md — customized for compliance workflows
validations:
required: true
- type: input
id: scripts-count
attributes:
label: Number of Scripts (optional)
description: How many scripts does your preset provide? (leave empty if none)
placeholder: "e.g., 1"
- type: textarea
id: tags

View File

@@ -1,59 +0,0 @@
name: "Catalog: Auto-assign submission"
on:
issues:
types: [opened, labeled]
jobs:
assign:
if: >
(github.event.action == 'opened' && (
contains(github.event.issue.labels.*.name, 'extension-submission') ||
contains(github.event.issue.labels.*.name, 'preset-submission')
)) ||
(github.event.action == 'labeled' && (
github.event.label.name == 'extension-submission' ||
github.event.label.name == 'preset-submission'
))
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/github-script@v7
with:
script: |
const issue = context.payload.issue;
const assigned = (issue.assignees || []).map(a => a.login);
const marker = '<!-- catalog-assign-bot -->';
// Assign mnriem if not already assigned
if (!assigned.includes('mnriem')) {
try {
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
assignees: ['mnriem'],
});
} catch (e) {
console.log(`Warning: could not assign mnriem: ${e.message}`);
}
}
// Post team notification if not already posted
const comments = await github.paginate(
github.rest.issues.listComments,
{
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
}
);
if (!comments.some(c => c.body && c.body.includes(marker))) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: marker + '\ncc @github/spec-kit-maintainers — new catalog submission for review.',
});
}

View File

@@ -19,14 +19,14 @@ jobs:
language: [ 'actions', 'python' ]
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"

View File

@@ -30,12 +30,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
with:
fetch-depth: 0 # Fetch all history for git info
- name: Setup .NET
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.x'
@@ -48,10 +48,10 @@ jobs:
docfx docfx.json
- name: Setup Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6
uses: actions/configure-pages@v6
- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
uses: actions/upload-pages-artifact@v5
with:
path: 'docs/_site'
@@ -66,4 +66,5 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
uses: actions/deploy-pages@v5

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
- name: Run markdownlint-cli2
uses: DavidAnson/markdownlint-cli2-action@6b51ade7a9e4a75a7ad929842dd298a3804ebe8b # v23

View File

@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_PAT }}

View File

@@ -12,7 +12,7 @@ jobs:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
@@ -86,3 +86,4 @@ jobs:
--notes-file release_notes.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -14,7 +14,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
- uses: actions/stale@v10
with:
# Days of inactivity before an issue or PR becomes stale
days-before-stale: 150

View File

@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
uses: actions/setup-python@v6
with:
python-version: "3.13"
@@ -34,13 +34,13 @@ jobs:
python-version: ["3.11", "3.12", "3.13"]
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

View File

@@ -2,36 +2,6 @@
<!-- insert new changelog below this comment -->
## [0.8.7] - 2026-05-07
### Changed
- feat: add agent-orchestrator to community extension catalog (#2236)
- chore: update extension versions in community catalog (#2468)
- fix(goose): Declare args parameter in generated recipes (#2402)
- feat: Add lingma support (#2348)
- docs: Add uv installation guide and inline callouts (#2465)
- Add fx-to-dotnet to community extension catalog (#2471)
- fix: default non-interactive init to copilot integration (#2414)
- fix(forge): use hyphen notation for command refs in Forge integration (#2462)
- feat(catalog): add Cost Tracker (cost) community extension (#2448)
- chore: release 0.8.6, begin 0.8.7.dev0 development (#2463)
## [0.8.6] - 2026-05-06
### Changed
- Load constitution context in `/speckit.implement` to enforce governance during implementation (#2460)
- feat: improve catalog submission templates and CODEOWNERS (#2401)
- fix: validate URL scheme in build_github_request (#2449)
- Add Architecture Guard to community catalog (#2430)
- Add multi-model-review extension to community catalog (#2446)
- Update Ralph Loop to v1.0.2 (#2435)
- Pin GitHub Actions by SHA (#2441)
- fix(workflows): require project for catalog list (#2436)
- Add agent-parity-governance to community catalog (#2382)
- chore: release 0.8.5, begin 0.8.6.dev0 development (#2447)
## [0.8.5] - 2026-05-04
### Changed

View File

@@ -56,9 +56,6 @@ Choose your preferred installation method:
Install once and use everywhere. Pin a specific release tag for stability (check [Releases](https://github.com/github/spec-kit/releases) for the latest):
> [!NOTE]
> The `uv tool install` commands below require **[uv](https://docs.astral.sh/uv/)** — a fast Python package manager. If you see `command not found: uv`, [install uv first](./docs/install/uv.md). The `pipx` alternative does not require uv.
```bash
# Install a specific stable release (recommended — replace vX.Y.Z with the latest tag)
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@vX.Y.Z
@@ -177,7 +174,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
## 🧩 Community Extensions
> [!NOTE]
> Community extensions are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion.
> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion.
🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).**
@@ -201,7 +198,6 @@ The following community-contributed extensions are available in [`catalog.commun
| Agent Assign | Assign specialized Claude Code agents to spec-kit tasks for targeted execution | `process` | Read+Write | [spec-kit-agent-assign](https://github.com/xymelon/spec-kit-agent-assign) |
| AI-Driven Engineering (AIDE) | A structured 7-step workflow for building new projects from scratch with AI assistants — from vision through implementation | `process` | Read+Write | [aide](https://github.com/mnriem/spec-kit-extensions/tree/main/aide) |
| Architect Impact Previewer | Predicts architectural impact, complexity, and risks of proposed changes before implementation. | `visibility` | Read-only | [spec-kit-architect-preview](https://github.com/UmmeHabiba1312/spec-kit-architect-preview) |
| Architecture Guard | Continuous architecture governance for AI-assisted development. Reviews specs, plans, and code for architecture drift, producing structured refactor tasks and evolution proposals. | `process` | Read+Write | [spec-kit-architecture-guard](https://github.com/DyanGalih/spec-kit-architecture-guard) |
| Archive Extension | Archive merged features into main project memory. | `docs` | Read+Write | [spec-kit-archive](https://github.com/stn1slv/spec-kit-archive) |
| Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | `integration` | Read+Write | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) |
| Blueprint | Stay code-literate in AI-driven development: review a complete code blueprint for every task from spec artifacts before /speckit.implement runs | `docs` | Read+Write | [spec-kit-blueprint](https://github.com/chordpli/spec-kit-blueprint) |
@@ -215,7 +211,6 @@ The following community-contributed extensions are available in [`catalog.commun
| Cleanup Extension | Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues | `code` | Read+Write | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) |
| Conduct Extension | Orchestrates spec-kit phases via sub-agent delegation to reduce context pollution. | `process` | Read+Write | [spec-kit-conduct-ext](https://github.com/twbrandon7/spec-kit-conduct-ext) |
| Confluence Extension | Create a doc in Confluence summarizing the specifications and planning files | `integration` | Read+Write | [spec-kit-confluence](https://github.com/aaronrsun/spec-kit-confluence) |
| Cost Tracker | Track real LLM dollar cost across SDD workflows — per-feature budgets, per-integration comparison, and finance-ready exports | `visibility` | Read+Write | [spec-kit-cost](https://github.com/Quratulain-bilal/spec-kit-cost) |
| DocGuard — CDD Enforcement | Canonical-Driven Development enforcement. Validates, scores, and traces project documentation with automated checks, AI-driven workflows, and spec-kit hooks. Zero NPM runtime dependencies. | `docs` | Read+Write | [spec-kit-docguard](https://github.com/raccioly/docguard) |
| Extensify | Create and validate extensions and extension catalogs | `process` | Read+Write | [extensify](https://github.com/mnriem/spec-kit-extensions/tree/main/extensify) |
| Fix Findings | Automated analyze-fix-reanalyze loop that resolves spec findings until clean | `code` | Read+Write | [spec-kit-fix-findings](https://github.com/Quratulain-bilal/spec-kit-fix-findings) |
@@ -223,7 +218,6 @@ The following community-contributed extensions are available in [`catalog.commun
| Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | `process` | Read+Write | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) |
| GitHub Issues Integration 1 | Generate spec artifacts from GitHub Issues - import issues, sync updates, and maintain bidirectional traceability | `integration` | Read+Write | [spec-kit-github-issues](https://github.com/Fatima367/spec-kit-github-issues) |
| GitHub Issues Integration 2 | Creates and syncs local specs from an existing GitHub issue | `integration` | Read+Write | [spec-kit-issue](https://github.com/aaronrsun/spec-kit-issue) |
| Intelligent Agent Orchestrator | Cross-catalog agent discovery and intelligent prompt-to-command routing | `process` | Read+Write | [spec-kit-orchestrator](https://github.com/pragya247/spec-kit-orchestrator) |
| Iterate | Iterate on spec documents with a two-phase define-and-apply workflow — refine specs mid-implementation and go straight back to building | `docs` | Read+Write | [spec-kit-iterate](https://github.com/imviancagrace/spec-kit-iterate) |
| Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | `integration` | Read+Write | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) |
| Learning Extension | Generate educational guides from implementations and enhance clarifications with mentoring context | `docs` | Read+Write | [spec-kit-learn](https://github.com/imviancagrace/spec-kit-learn) |
@@ -239,8 +233,6 @@ The following community-contributed extensions are available in [`catalog.commun
| Memory MD | Spec Kit extension for repository-native Markdown memory that captures durable decisions, bugs, and project context | `docs` | Read+Write | [spec-kit-memory-hub](https://github.com/DyanGalih/spec-kit-memory-hub) |
| MemoryLint | Agent memory governance tool: Automatically audits and fixes boundary conflicts between AGENTS.md and the constitution. | `process` | Read+Write | [memorylint](https://github.com/RbBtSn0w/spec-kit-extensions/tree/main/memorylint) |
| Microsoft 365 Integration | Fetch Teams messages, meeting transcripts, and SharePoint/OneDrive files as local Markdown for spec generation | `integration` | Read+Write | [spec-kit-m365](https://github.com/BenBtg/spec-kit-m365) |
| Multi-Model Review | Cross-model Spec Kit handoffs for spec authoring, implementation routing, and review. | `process` | Read+Write | [multi-model-review](https://github.com/formin/multi-model-review) |
| .NET Framework to Modern .NET Migration | Orchestrate end-to-end .NET Framework to modern .NET migration across 7 phases, with SDD lifecycle integration | `process` | Read+Write | [spec-kit-fx-to-net](https://github.com/RogerBestMsft/spec-kit-FxToNet) |
| Onboard | Contextual onboarding and progressive growth for developers new to spec-kit projects. Explains specs, maps dependencies, validates understanding, and guides the next step | `process` | Read+Write | [spec-kit-onboard](https://github.com/dmux/spec-kit-onboard) |
| Optimize | Audit and optimize AI governance for context efficiency — token budgets, rule health, interpretability, compression, coherence, and echo detection | `process` | Read+Write | [spec-kit-optimize](https://github.com/sakitA/spec-kit-optimize) |
| OWASP LLM Threat Model | OWASP Top 10 for LLM Applications 2025 threat analysis on agent artifacts | `code` | Read-only | [spec-kit-threatmodel](https://github.com/NaviaSamal/spec-kit-threatmodel) |
@@ -251,7 +243,7 @@ The following community-contributed extensions are available in [`catalog.commun
| Project Health Check | Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git | `visibility` | Read-only | [spec-kit-doctor](https://github.com/KhawarHabibKhan/spec-kit-doctor) |
| Project Status | Show current SDD workflow progress — active feature, artifact status, task completion, workflow phase, and extensions summary | `visibility` | Read-only | [spec-kit-status](https://github.com/KhawarHabibKhan/spec-kit-status) |
| QA Testing Extension | Systematic QA testing with browser-driven or CLI-based validation of acceptance criteria from spec | `code` | Read-only | [spec-kit-qa](https://github.com/arunt14/spec-kit-qa) |
| Ralph Loop | Autonomous implementation loop using AI agent CLI | `code` | Read+Write | [spec-kit-ralph](https://github.com/Rubiss-Projects/spec-kit-ralph) |
| Ralph Loop | Autonomous implementation loop using AI agent CLI | `code` | Read+Write | [spec-kit-ralph](https://github.com/Rubiss/spec-kit-ralph) |
| Reconcile Extension | Reconcile implementation drift by surgically updating feature artifacts. | `docs` | Read+Write | [spec-kit-reconcile](https://github.com/stn1slv/spec-kit-reconcile) |
| Red Team | Adversarial review of specs before /speckit.plan — parallel lens agents surface risks that clarify/analyze structurally can't (prompt injection, integrity gaps, cross-spec drift, silent failures). Produces a structured findings report; no auto-edits to specs. | `docs` | Read+Write | [spec-kit-red-team](https://github.com/ashbrener/spec-kit-red-team) |
| Repository Index | Generate index for existing repo for overview, architecture and module level. | `docs` | Read-only | [spec-kit-repoindex](https://github.com/liuyiyu/spec-kit-repoindex) |
@@ -490,7 +482,7 @@ specify init --here --force
![Specify CLI bootstrapping a new project in the terminal](./media/specify_cli.gif)
In an interactive terminal, you will be prompted to select the coding agent integration you are using. In non-interactive sessions, such as CI or piped runs, `specify init` defaults to GitHub Copilot unless you pass `--integration`. You can also proactively specify the integration directly in the terminal:
You will be prompted to select the coding agent integration you are using. You can also proactively specify it directly in the terminal:
```bash
specify init <project_name> --integration copilot

View File

@@ -1,14 +1,13 @@
# Community Presets
> [!NOTE]
> Community presets are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion.
> Community presets are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion.
The following community-contributed presets customize how Spec Kit behaves — overriding templates, commands, and terminology without changing any tooling. Presets are available in [`catalog.community.json`](https://github.com/github/spec-kit/blob/main/presets/catalog.community.json):
| Preset | Purpose | Provides | Requires | URL |
|--------|---------|----------|----------|-----|
| A11Y Governance | Adds WCAG 2.2 AA accessibility checks, bilingual DE/EN delivery, CEFR-B2 readability, CLI accessibility, and inclusive-content guidance | 9 templates, 3 commands | — | [spec-kit-preset-a11y-governance](https://github.com/hindermath/spec-kit-preset-a11y-governance) |
| Agent Parity Governance | Keeps shared AI-agent instructions aligned across project-defined agent guidance surfaces and documents intentional deviations | 6 templates, 3 commands | — | [spec-kit-preset-agent-parity-governance](https://github.com/hindermath/spec-kit-preset-agent-parity-governance) |
| AIDE In-Place Migration | Adapts the AIDE extension workflow for in-place technology migrations (X → Y pattern) — adds migration objectives, verification gates, knowledge documents, and behavioral equivalence criteria | 2 templates, 8 commands | AIDE extension | [spec-kit-presets](https://github.com/mnriem/spec-kit-presets) |
| Architecture Governance | Adds secure architecture governance: trust boundaries, threat modeling, STRIDE/CAPEC, S-ADRs, Zero Trust applicability, and OWASP SAMM | 11 templates, 3 commands | — | [spec-kit-preset-architecture-governance](https://github.com/hindermath/spec-kit-preset-architecture-governance) |
| Canon Core | Adapts original Spec Kit workflow to work together with Canon extension | 2 templates, 8 commands | — | [spec-kit-canon](https://github.com/maximiliamus/spec-kit-canon) |

View File

@@ -1,60 +0,0 @@
# Installing uv
[uv](https://docs.astral.sh/uv/) is a fast Python package manager by [Astral](https://astral.sh/). Spec Kit uses `uv` (via `uvx` or `uv tool install`) to run the `specify` CLI without polluting your global Python environment.
> [!NOTE]
> **Already have uv?** Run `uv --version` to confirm it is installed, then head back to the [Installation Guide](../installation.md).
## Installation
### macOS and Linux — Standalone Installer
The quickest way to install uv on macOS or Linux is the official shell script:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
After the script finishes, follow any instructions printed by the installer to add uv to your `PATH`, then open a new terminal.
### Windows — Standalone Installer
Run the following in **Command Prompt or PowerShell**:
```powershell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
After the script finishes, open a new terminal so the `uv` binary is on your `PATH`.
### macOS — Homebrew
```bash
brew install uv
```
### Windows — WinGet
```powershell
winget install --id=astral-sh.uv -e
```
### Windows — Scoop
```powershell
scoop install uv
```
## Verification
Confirm that uv is installed and on your `PATH`:
```bash
uv --version
```
You should see output similar to `uv 0.x.y (...)`.
## Further Reading
For advanced options (self-update, proxy settings, uninstall, etc.) see the official [uv installation docs](https://docs.astral.sh/uv/getting-started/installation/).

View File

@@ -16,9 +16,6 @@
The easiest way to get started is to initialize a new project. Pin a specific release tag for stability (check [Releases](https://github.com/github/spec-kit/releases) for the latest):
> [!NOTE]
> The `uvx` commands below require **[uv](https://docs.astral.sh/uv/)**. If you see `command not found: uvx`, [install uv first](./install/uv.md). The `pipx` alternative does not require uv.
```bash
# Install from a specific stable release (recommended — replace vX.Y.Z with the latest tag)
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init <PROJECT_NAME>
@@ -44,8 +41,6 @@ uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here
### Specify Integration
Interactive terminals prompt you to choose a coding agent integration during initialization. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot unless you pass `--integration`.
You can proactively specify your coding agent integration during initialization:
```bash

View File

@@ -28,8 +28,6 @@ Creates a new Spec Kit project with the necessary directory structure, templates
Use `<project_name>` to create a new directory, or `--here` (or `.`) to initialize in the current directory. If the directory already has files, use `--force` to merge without confirmation.
When `--integration` is omitted, interactive terminals prompt you to choose an integration. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot; pass `--integration <key>` to choose a different integration explicitly.
### Examples
```bash

View File

@@ -24,7 +24,6 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | `kilocode` | |
| [Kimi Code](https://code.kimi.com/) | `kimi` | Skills-based integration; supports `--migrate-legacy` for dotted→hyphenated directory migration |
| [Kiro CLI](https://kiro.dev/docs/cli/) | `kiro-cli` | Alias: `--integration kiro` |
| [Lingma](https://lingma.aliyun.com/) | `lingma` | Skills-based integration; skills are installed automatically |
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | `vibe` | |
| [opencode](https://opencode.ai/) | `opencode` | |
| [Pi Coding Agent](https://pi.dev) | `pi` | 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) |

View File

@@ -11,8 +11,6 @@
href: quickstart.md
- name: Upgrade
href: upgrade.md
- name: Install uv
href: install/uv.md
# Reference
- name: Reference

View File

@@ -528,9 +528,11 @@ specify extension add <extension-name> --from https://github.com/.../spec-kit-my
Submit to the community catalog for public discovery:
1. **Create a GitHub release** for your extension
2. **File an issue** using the [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) template
3. **After review**, a maintainer updates the catalog and your extension becomes available:
1. **Fork** spec-kit repository
2. **Add entry** to `extensions/catalog.community.json`
3. **Update** the Community Extensions table in `README.md` with your extension
4. **Create PR** following the [Extension Publishing Guide](EXTENSION-PUBLISHING-GUIDE.md)
5. **After merge**, your extension becomes available:
- Users can browse `catalog.community.json` to discover your extension
- Users copy the entry to their own `catalog.json`
- Users install with: `specify extension add my-ext` (from their catalog)

View File

@@ -7,8 +7,9 @@ This guide explains how to publish your extension to the Spec Kit extension cata
1. [Prerequisites](#prerequisites)
2. [Prepare Your Extension](#prepare-your-extension)
3. [Submit to Catalog](#submit-to-catalog)
4. [Release Workflow](#release-workflow)
5. [Best Practices](#best-practices)
4. [Verification Process](#verification-process)
5. [Release Workflow](#release-workflow)
6. [Best Practices](#best-practices)
---
@@ -132,46 +133,222 @@ specify extension add <extension-name> --from https://github.com/your-org/spec-k
Spec Kit uses a dual-catalog system. For details about how catalogs work, see the main [Extensions README](README.md#extension-catalogs).
**For extension publishing**: All community extensions are listed in `extensions/catalog.community.json`. Users browse this catalog and copy extensions they trust into their own `catalog.json`.
**For extension publishing**: All community extensions should be added to `catalog.community.json`. Users browse this catalog and copy extensions they trust into their own `catalog.json`.
### How to Submit
### 1. Fork the spec-kit Repository
To submit your extension to the community catalog, file a new issue using the **[Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml)** template. The template collects all required metadata, including:
```bash
# Fork on GitHub
# https://github.com/github/spec-kit/fork
- Extension ID, name, and version
- Description, author, and license
- Repository, download URL, and documentation links
- Required Spec Kit version and any tool dependencies
- Number of commands and hooks
- Tags and key features
- Testing confirmation
# Clone your fork
git clone https://github.com/YOUR-USERNAME/spec-kit.git
cd spec-kit
```
> [!IMPORTANT]
> Do **not** open a pull request directly to edit `extensions/catalog.community.json`. All community extension submissions must go through the issue template so a maintainer can review the entry and update the catalog.
### 2. Add Extension to Community Catalog
### What Happens After You Submit
Edit `extensions/catalog.community.json` and add your extension:
1. Your issue is automatically labeled and assigned to a maintainer for review
2. A maintainer verifies that the catalog entry is complete and correctly formatted
3. Once approved, the maintainer adds your extension to `extensions/catalog.community.json` and the Community Extensions table in the README
4. Your extension becomes discoverable via `specify extension search`
```json
{
"schema_version": "1.0",
"updated_at": "2026-01-28T15:54:00Z",
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
"extensions": {
"your-extension": {
"name": "Your Extension Name",
"id": "your-extension",
"description": "Brief description of your extension",
"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",
"documentation": "https://github.com/your-org/spec-kit-your-extension/blob/main/docs/",
"changelog": "https://github.com/your-org/spec-kit-your-extension/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
{
"name": "required-mcp-tool",
"version": ">=1.0.0",
"required": true
}
]
},
"provides": {
"commands": 3,
"hooks": 1
},
"tags": [
"category",
"tool-name",
"feature"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-01-28T00:00:00Z",
"updated_at": "2026-01-28T00:00:00Z"
}
}
}
```
### What Maintainers Check
**Important**:
- The catalog entry fields are complete and correctly formatted
- The download URL is accessible
- The repository exists and contains an `extension.yml` manifest
- Set `verified: false` (maintainers will verify)
- Set `downloads: 0` and `stars: 0` (auto-updated later)
- Use current timestamp for `created_at` and `updated_at`
- Update the top-level `updated_at` to current time
> [!NOTE]
> Maintainers do **not** review, audit, or test the extension code itself.
### 3. Update Community Extensions Table
Add your extension to the Community Extensions table in the project root `README.md`:
```markdown
| Your Extension Name | Brief description of what it does | `<category>` | <effect> | [repo-name](https://github.com/your-org/spec-kit-your-extension) |
```
**(Table) Category** — pick the one that best fits your extension:
- `docs` — reads, validates, or generates spec artifacts
- `code` — reviews, validates, or modifies source code
- `process` — orchestrates workflow across phases
- `integration` — syncs with external platforms
- `visibility` — reports on project health or progress
**Effect** — choose one:
- Read-only — produces reports without modifying files
- Read+Write — modifies files, creates artifacts, or updates specs
Insert your extension in alphabetical order in the table.
### 4. Submit Pull Request
```bash
# Create a branch
git checkout -b add-your-extension
# Commit your changes
git add extensions/catalog.community.json README.md
git commit -m "Add your-extension to community catalog
- Extension ID: your-extension
- Version: 1.0.0
- Author: Your Name
- Description: Brief description
"
# Push to your fork
git push origin add-your-extension
# Create Pull Request on GitHub
# https://github.com/github/spec-kit/compare
```
**Pull Request Template**:
```markdown
## Extension Submission
**Extension Name**: Your Extension Name
**Extension ID**: your-extension
**Version**: 1.0.0
**Author**: Your Name
**Repository**: https://github.com/your-org/spec-kit-your-extension
### Description
Brief description of what your extension does.
### Checklist
- [x] Valid extension.yml manifest
- [x] README.md with installation and usage docs
- [x] LICENSE file included
- [x] GitHub release created (v1.0.0)
- [x] Extension tested on real project
- [x] All commands working
- [x] No security vulnerabilities
- [x] Added to extensions/catalog.community.json
- [x] Added to Community Extensions table in README.md
### Testing
Tested on:
- macOS 13.0+ with spec-kit 0.1.0
- Project: [Your test project]
### Additional Notes
Any additional context or notes for reviewers.
```
---
## Verification Process
### What Happens After Submission
1. **Automated Checks** (if available):
- Manifest validation
- Download URL accessibility
- Repository existence
- License file presence
2. **Manual Review**:
- Code quality review
- Security audit
- Functionality testing
- Documentation review
3. **Verification**:
- If approved, `verified: true` is set
- Extension appears in `specify extension search --verified`
### Verification Criteria
To be verified, your extension must:
**Functionality**:
- Works as described in documentation
- All commands execute without errors
- No breaking changes to user workflows
**Security**:
- No known vulnerabilities
- No malicious code
- Safe handling of user data
- Proper validation of inputs
**Code Quality**:
- Clean, readable code
- Follows extension best practices
- Proper error handling
- Helpful error messages
**Documentation**:
- Clear installation instructions
- Usage examples
- Troubleshooting section
- Accurate description
**Maintenance**:
- Active repository
- Responsive to issues
- Regular updates
- Semantic versioning followed
### Typical Review Timeline
- **Review**: 3-7 business days
### Updating an Existing Extension
To update an extension that is already in the catalog (e.g., for a new version), file a new **[Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml)** issue with the updated version, download URL, and any other changed fields. Mention in the issue that this is an update to an existing entry.
- **Automated checks**: Immediate (if implemented)
- **Manual review**: 3-7 business days
- **Verification**: After successful review
---
@@ -208,7 +385,26 @@ When releasing a new version:
# Create release on GitHub
```
4. **File an update submission** using the [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) template with the new version and download URL. Mention in the issue that this is an update to an existing entry.
4. **Update catalog**:
```bash
# Fork spec-kit repo (or update existing fork)
cd spec-kit
# Update extensions/catalog.json
jq '.extensions["your-extension"].version = "1.1.0"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
jq '.extensions["your-extension"].download_url = "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.1.0.zip"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
jq '.extensions["your-extension"].updated_at = "2026-02-15T00:00:00Z"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
jq '.updated_at = "2026-02-15T00:00:00Z"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
# Submit PR
git checkout -b update-your-extension-v1.1.0
git add extensions/catalog.json
git commit -m "Update your-extension to v1.1.0"
git push origin update-your-extension-v1.1.0
```
5. **Submit update PR** with changelog in description
---
@@ -277,9 +473,9 @@ A: The main catalog is for public extensions only. For private extensions:
- Users add your catalog: `specify extension add-catalog https://your-domain.com/catalog.json`
- Not yet implemented - coming in Phase 4
### Q: How long does review take?
### Q: How long does verification take?
A: Typically 3-7 business days. Updates to existing extensions are usually faster.
A: Typically 3-7 business days for initial review. Updates to verified extensions are usually faster.
### Q: What if my extension is rejected?
@@ -287,11 +483,11 @@ A: You'll receive feedback on what needs to be fixed. Make the changes and resub
### Q: Can I update my extension anytime?
A: Yes, file a new [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) issue with the updated version and download URL. Mention that it is an update to an existing entry.
A: Yes, submit a PR to update the catalog with your new version. Verified status may be re-evaluated for major changes.
### Q: Do I need to be verified to be in the catalog?
A: No. All community extensions are listed in the catalog once their submission is reviewed and accepted.
A: No, unverified extensions are still searchable. Verification just adds trust and visibility.
### Q: Can extensions have paid features?
@@ -340,7 +536,7 @@ A: Extensions should be free and open-source. Commercial support/services are al
"hooks": "integer (optional)"
},
"tags": ["array of strings (2-10 tags)"],
"verified": "boolean (default: false, set by maintainers)",
"verified": "boolean (default: false)",
"downloads": "integer (auto-updated)",
"stars": "integer (auto-updated)",
"created_at": "string (ISO 8601 datetime)",

View File

@@ -25,13 +25,13 @@ specify extension search # Now uses your organization's catalog instead of the
### Community Reference Catalog (`catalog.community.json`)
> [!NOTE]
> Community extensions are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do **not review, audit, endorse, or support the extension code itself**. Review extension source code before installation and use at your own discretion.
> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. Review extension source code before installation and use at your own discretion.
- **Purpose**: Browse available community-contributed extensions
- **Status**: Active - contains extensions submitted by the community
- **Location**: `extensions/catalog.community.json`
- **Usage**: Reference catalog for discovering available extensions
- **Submission**: Open to community contributions via [issue template](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml)
- **Submission**: Open to community contributions via Pull Request
**How It Works:**
@@ -72,7 +72,7 @@ specify extension add <extension-name> --from https://github.com/org/spec-kit-ex
## Available Community Extensions
> [!NOTE]
> Community extensions are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion.
> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion.
🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).**
@@ -89,8 +89,10 @@ To add your extension to the community catalog:
1. **Prepare your extension** following the [Extension Development Guide](EXTENSION-DEVELOPMENT-GUIDE.md)
2. **Create a GitHub release** for your extension
3. **File an issue** using the [Extension Submission](https://github.com/github/spec-kit/issues/new?template=extension_submission.yml) template with all required metadata
4. **Wait for review** — a maintainer will review the submission, update the catalog, and close the issue
3. **Submit a Pull Request** that:
- Adds your extension to `extensions/catalog.community.json`
- Updates this README with your extension in the Available Extensions table
4. **Wait for review** - maintainers will review and merge if criteria are met
See the [Extension Publishing Guide](EXTENSION-PUBLISHING-GUIDE.md) for detailed step-by-step instructions.

View File

@@ -1,6 +1,6 @@
{
"schema_version": "1.0",
"updated_at": "2026-05-07T05:51:00Z",
"updated_at": "2026-05-03T00:00:00Z",
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
"extensions": {
"aide": {
@@ -68,38 +68,6 @@
"created_at": "2026-03-31T00:00:00Z",
"updated_at": "2026-03-31T00:00:00Z"
},
"agent-orchestrator": {
"name": "Intelligent Agent Orchestrator",
"id": "agent-orchestrator",
"description": "Cross-catalog agent discovery and intelligent prompt-to-command routing",
"author": "pragya247",
"version": "0.1.0",
"download_url": "https://github.com/pragya247/spec-kit-orchestrator/archive/refs/tags/v0.1.0.zip",
"repository": "https://github.com/pragya247/spec-kit-orchestrator",
"homepage": "https://github.com/pragya247/spec-kit-orchestrator",
"documentation": "https://github.com/pragya247/spec-kit-orchestrator/blob/main/README.md",
"changelog": "https://github.com/pragya247/spec-kit-orchestrator/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.6.1"
},
"provides": {
"commands": 3,
"hooks": 1
},
"tags": [
"orchestrator",
"routing",
"discovery",
"agent",
"ai"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-05-04T00:00:00Z",
"updated_at": "2026-05-04T00:00:00Z"
},
"architect-preview": {
"name": "Architect Impact Previewer",
"id": "architect-preview",
@@ -132,39 +100,6 @@
"created_at": "2026-04-14T00:00:00Z",
"updated_at": "2026-04-14T00:00:00Z"
},
"architecture-guard": {
"name": "Architecture Guard",
"id": "architecture-guard",
"description": "Continuous architecture governance for AI-assisted development. Reviews specs, plans, and code for architecture drift, producing structured refactor tasks and evolution proposals.",
"author": "DyanGalih",
"version": "1.6.7",
"download_url": "https://github.com/DyanGalih/spec-kit-architecture-guard/archive/refs/tags/v1.6.7.zip",
"repository": "https://github.com/DyanGalih/spec-kit-architecture-guard",
"homepage": "https://github.com/DyanGalih/spec-kit-architecture-guard",
"documentation": "https://github.com/DyanGalih/spec-kit-architecture-guard/blob/main/README.md",
"changelog": "https://github.com/DyanGalih/spec-kit-architecture-guard/releases",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0"
},
"provides": {
"commands": 6,
"hooks": 0
},
"tags": [
"architecture",
"governance",
"drift-detection",
"refactor",
"monolithic",
"microservices"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-05-05T07:26:00Z",
"updated_at": "2026-05-06T22:28:55Z"
},
"archive": {
"name": "Archive Extension",
"id": "archive",
@@ -612,38 +547,6 @@
"created_at": "2026-03-29T00:00:00Z",
"updated_at": "2026-03-29T00:00:00Z"
},
"cost": {
"name": "Cost Tracker",
"id": "cost",
"description": "Track real LLM dollar cost across SDD workflows — per-feature budgets, per-integration comparison, and finance-ready exports.",
"author": "Quratulain-bilal",
"version": "1.0.0",
"download_url": "https://github.com/Quratulain-bilal/spec-kit-cost/archive/refs/tags/v1.0.0.zip",
"repository": "https://github.com/Quratulain-bilal/spec-kit-cost",
"homepage": "https://github.com/Quratulain-bilal/spec-kit-cost",
"documentation": "https://github.com/Quratulain-bilal/spec-kit-cost/blob/main/README.md",
"changelog": "https://github.com/Quratulain-bilal/spec-kit-cost/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.8.0"
},
"provides": {
"commands": 5,
"hooks": 0
},
"tags": [
"cost",
"budget",
"tokens",
"visibility",
"finance"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-05-03T00:00:00Z",
"updated_at": "2026-05-05T00:00:00Z"
},
"diagram": {
"name": "Spec Diagram",
"id": "diagram",
@@ -874,44 +777,6 @@
"created_at": "2026-03-06T00:00:00Z",
"updated_at": "2026-03-31T00:00:00Z"
},
"fx-to-dotnet": {
"name": ".NET Framework to Modern .NET Migration",
"id": "fx-to-dotnet",
"description": "Orchestrate end-to-end .NET Framework to modern .NET migration across 7 phases, with SDD lifecycle integration.",
"author": "RogerBestMsft",
"version": "0.8.0",
"download_url": "https://github.com/RogerBestMsft/spec-kit-FxToNet/releases/download/v0.8.0/fx-to-dotnet.zip",
"repository": "https://github.com/RogerBestMsft/spec-kit-FxToNet",
"homepage": "https://github.com/RogerBestMsft/spec-kit-FxToNet",
"documentation": "https://github.com/RogerBestMsft/spec-kit-FxToNet/blob/main/README.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
{
"name": "Microsoft.GitHubCopilot.Modernization.Mcp",
"required": true
}
]
},
"provides": {
"commands": 12,
"hooks": 5
},
"tags": [
"dotnet",
"migration",
"modernization",
"framework",
"aspnet",
"shared-artifact"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-05-06T00:00:00Z",
"updated_at": "2026-05-06T00:00:00Z"
},
"github-issues": {
"name": "GitHub Issues Integration 1",
"id": "github-issues",
@@ -1415,8 +1280,8 @@
"id": "memory-md",
"description": "Spec Kit extension for repository-native Markdown memory that captures durable decisions, bugs, and project context",
"author": "DyanGalih",
"version": "0.7.9",
"download_url": "https://github.com/DyanGalih/spec-kit-memory-hub/archive/refs/tags/v0.7.9.zip",
"version": "0.7.5",
"download_url": "https://github.com/DyanGalih/spec-kit-memory-hub/archive/refs/tags/v0.7.5.zip",
"repository": "https://github.com/DyanGalih/spec-kit-memory-hub",
"homepage": "https://github.com/DyanGalih/spec-kit-memory-hub",
"documentation": "https://github.com/DyanGalih/spec-kit-memory-hub/blob/main/README.md",
@@ -1441,7 +1306,7 @@
"downloads": 0,
"stars": 0,
"created_at": "2026-04-23T00:00:00Z",
"updated_at": "2026-05-06T22:28:55Z"
"updated_at": "2026-05-03T00:00:00Z"
},
"memorylint": {
"name": "MemoryLint",
@@ -1475,56 +1340,6 @@
"created_at": "2026-04-09T00:00:00Z",
"updated_at": "2026-04-16T13:10:26Z"
},
"multi-model-review": {
"name": "Multi-Model Review",
"id": "multi-model-review",
"description": "Cross-model Spec Kit handoffs for spec authoring, implementation routing, and review.",
"author": "formin",
"version": "0.1.0",
"download_url": "https://github.com/formin/multi-model-review/archive/refs/tags/v0.1.0.zip",
"repository": "https://github.com/formin/multi-model-review",
"homepage": "https://github.com/formin/multi-model-review",
"documentation": "https://github.com/formin/multi-model-review/blob/main/README.md",
"changelog": "https://github.com/formin/multi-model-review/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.2.0",
"tools": [
{
"name": "git",
"required": true
},
{
"name": "codex",
"required": false
},
{
"name": "gemini",
"required": false
},
{
"name": "claude",
"required": false
}
]
},
"provides": {
"commands": 4,
"hooks": 0
},
"tags": [
"review",
"workflow",
"multi-model",
"spec-driven-development",
"code"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-05-04T02:51:52Z",
"updated_at": "2026-05-04T02:51:52Z"
},
"onboard": {
"name": "Onboard",
"id": "onboard",
@@ -1783,12 +1598,12 @@
"id": "ralph",
"description": "Autonomous implementation loop using AI agent CLI.",
"author": "Rubiss",
"version": "1.0.2",
"download_url": "https://github.com/Rubiss-Projects/spec-kit-ralph/archive/refs/tags/v1.0.2.zip",
"repository": "https://github.com/Rubiss-Projects/spec-kit-ralph",
"homepage": "https://github.com/Rubiss-Projects/spec-kit-ralph",
"documentation": "https://github.com/Rubiss-Projects/spec-kit-ralph/blob/main/README.md",
"changelog": "https://github.com/Rubiss-Projects/spec-kit-ralph/blob/main/CHANGELOG.md",
"version": "1.0.1",
"download_url": "https://github.com/Rubiss/spec-kit-ralph/archive/refs/tags/v1.0.1.zip",
"repository": "https://github.com/Rubiss/spec-kit-ralph",
"homepage": "https://github.com/Rubiss/spec-kit-ralph",
"documentation": "https://github.com/Rubiss/spec-kit-ralph/blob/main/README.md",
"changelog": "https://github.com/Rubiss/spec-kit-ralph/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0",
@@ -1817,7 +1632,7 @@
"downloads": 0,
"stars": 0,
"created_at": "2026-03-09T00:00:00Z",
"updated_at": "2026-05-04T17:02:08Z"
"updated_at": "2026-04-12T19:00:00Z"
},
"reconcile": {
"name": "Reconcile Extension",
@@ -2117,8 +1932,8 @@
"id": "security-review",
"description": "Full-project secure-by-design security audits plus staged, branch/PR, plan, task, follow-up, and apply reviews",
"author": "DyanGalih",
"version": "1.4.5",
"download_url": "https://github.com/DyanGalih/spec-kit-security-review/archive/refs/tags/v1.4.5.zip",
"version": "1.4.2",
"download_url": "https://github.com/DyanGalih/spec-kit-security-review/archive/refs/tags/v1.4.2.zip",
"repository": "https://github.com/DyanGalih/spec-kit-security-review",
"homepage": "https://github.com/DyanGalih/spec-kit-security-review",
"documentation": "https://github.com/DyanGalih/spec-kit-security-review/blob/main/README.md",
@@ -2142,7 +1957,7 @@
"downloads": 0,
"stars": 0,
"created_at": "2026-04-03T03:24:03Z",
"updated_at": "2026-05-06T22:28:55Z"
"updated_at": "2026-05-03T00:00:00Z"
},
"sf": {
"name": "SFSpeckit — Salesforce Spec-Driven Development",
@@ -3007,7 +2822,7 @@
"downloads": 0,
"stars": 0,
"created_at": "2026-04-13T00:00:00Z",
"updated_at": "2026-04-13T00:00:00Z"
"updated_at": "2026-04-13T00:00:00Z"
}
}
}

View File

@@ -4,7 +4,7 @@ description: "Create a feature branch with sequential or timestamp numbering"
# Create Feature Branch
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `__SPECKIT_COMMAND_SPECIFY__` workflow.
Create and switch to a new git feature branch for the given specification. This command handles **branch creation only** — the spec directory and files are created by the core `/speckit.specify` workflow.
## User Input

View File

@@ -1,6 +1,6 @@
{
"schema_version": "1.0",
"updated_at": "2026-04-29T00:00:00Z",
"updated_at": "2026-04-28T00:00:00Z",
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.json",
"integrations": {
"claude": {
@@ -210,15 +210,6 @@
"repository": "https://github.com/github/spec-kit",
"tags": ["cli", "skills"]
},
"lingma": {
"id": "lingma",
"name": "Lingma",
"version": "1.0.0",
"description": "Lingma IDE skills-based integration",
"author": "spec-kit-core",
"repository": "https://github.com/github/spec-kit",
"tags": ["ide", "skills"]
},
"pi": {
"id": "pi",
"name": "Pi Coding Agent",

View File

@@ -98,7 +98,7 @@ Multiple composing presets chain recursively. For example, a security preset wit
Presets are discovered through catalogs. By default, Spec Kit uses the official and community catalogs:
> [!NOTE]
> Community presets are independently created and maintained by their respective authors. Maintainers only verify that catalog entries are complete and correctly formatted — they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion.
> Community presets are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion.
```bash
# List active catalogs

View File

@@ -31,34 +31,6 @@
"created_at": "2026-04-27T00:00:00Z",
"updated_at": "2026-04-27T00:00:00Z"
},
"agent-parity-governance": {
"name": "Agent Parity Governance",
"id": "agent-parity-governance",
"version": "0.1.0",
"description": "Keeps shared AI-agent guidance aligned across a project-defined set of agent instruction surfaces.",
"author": "Thorsten Hindermann",
"repository": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance",
"download_url": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/archive/refs/tags/v0.1.0.zip",
"homepage": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance",
"documentation": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/blob/main/README.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.8.0"
},
"provides": {
"templates": 6,
"commands": 3
},
"tags": [
"agents",
"governance",
"parity",
"agent-guidance",
"multi-agent"
],
"created_at": "2026-04-27T00:00:00Z",
"updated_at": "2026-04-27T00:00:00Z"
},
"aide-in-place": {
"name": "AIDE In-Place Migration",
"id": "aide-in-place",

View File

@@ -1,6 +1,6 @@
[project]
name = "specify-cli"
version = "0.8.7"
version = "0.8.5"
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
requires-python = ">=3.11"
dependencies = [

View File

@@ -90,7 +90,6 @@ def _build_agent_config() -> dict[str, dict[str, Any]]:
return config
AGENT_CONFIG = _build_agent_config()
DEFAULT_INIT_INTEGRATION = "copilot"
AI_ASSISTANT_ALIASES = {
"kiro": "kiro-cli",
@@ -153,9 +152,6 @@ def _build_ai_deprecation_warning(
f"Use [bold]{replacement}[/bold] instead."
)
def _stdin_is_interactive() -> bool:
return sys.stdin.isatty()
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
@@ -999,8 +995,7 @@ def init(
This command will:
1. Check that required tools are installed (git is optional)
2. Let you choose your coding agent integration, or default to Copilot
in non-interactive sessions
2. Let you choose your coding agent integration
3. Download template from GitHub (or use bundled assets with --offline)
4. Initialize a fresh git repository (if not --no-git and no existing repo)
5. Optionally set up coding agent integration commands
@@ -1167,19 +1162,13 @@ def init(
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AGENT_CONFIG.keys())}")
raise typer.Exit(1)
selected_ai = ai_assistant
elif not _stdin_is_interactive():
console.print(
f"[dim]Non-interactive session detected: defaulting to '{DEFAULT_INIT_INTEGRATION}'. "
"Use --integration to choose a different agent.[/dim]"
)
selected_ai = DEFAULT_INIT_INTEGRATION
else:
# Create options dict for selection (agent_key: display_name)
ai_choices = {key: config["name"] for key, config in AGENT_CONFIG.items()}
selected_ai = select_with_arrows(
ai_choices,
"Choose your coding agent integration:",
DEFAULT_INIT_INTEGRATION,
"copilot"
)
# Auto-promote interactively selected agents to the integration path
@@ -1244,7 +1233,7 @@ def init(
else:
default_script = "ps" if os.name == "nt" else "sh"
if _stdin_is_interactive():
if sys.stdin.isatty():
selected_script = select_with_arrows(SCRIPT_TYPE_CHOICES, "Choose script type (or press Enter)", default_script)
else:
selected_script = default_script
@@ -5817,7 +5806,7 @@ def workflow_catalog_list():
"""List configured workflow catalog sources."""
from .workflows.catalog import WorkflowCatalog, WorkflowCatalogError
project_root = _require_specify_project()
project_root = Path.cwd()
catalog = WorkflowCatalog(project_root)
try:

View File

@@ -8,8 +8,8 @@ third-party hosts on redirects.
import os
import urllib.request
from typing import Dict
from urllib.parse import urlparse
from typing import Dict
# GitHub-owned hostnames that should receive the Authorization header.
# Includes codeload.github.com because GitHub archive URL downloads
@@ -30,25 +30,12 @@ def build_github_request(url: str) -> urllib.request.Request:
``Authorization: Bearer <value>`` header when the target hostname is one
of the known GitHub-owned domains. Non-GitHub URLs are returned as plain
requests so credentials are never leaked to third-party hosts.
Raises:
ValueError: If ``url`` is empty or whitespace-only.
ValueError: If ``url`` does not use the ``http`` or ``https`` scheme.
ValueError: If ``url`` does not include a hostname.
"""
headers: Dict[str, str] = {}
url = url.strip()
if not url:
raise ValueError("url must not be empty")
parsed = urlparse(url)
if parsed.scheme not in {"http", "https"}:
raise ValueError(f"url must start with http:// or https://, got: {url!r}")
if not parsed.hostname:
raise ValueError(f"url must include a hostname, got: {url!r}")
github_token = (os.environ.get("GITHUB_TOKEN") or "").strip()
gh_token = (os.environ.get("GH_TOKEN") or "").strip()
token = github_token or gh_token or None
hostname = parsed.hostname.lower()
hostname = (urlparse(url).hostname or "").lower()
if token and hostname in GITHUB_HOSTS:
headers["Authorization"] = f"Bearer {token}"
return urllib.request.Request(url, headers=headers)

View File

@@ -7,12 +7,12 @@ command files into agent-specific directories in the correct format.
"""
import os
from pathlib import Path
from typing import Dict, List, Any, Optional
import platform
import re
from copy import deepcopy
from pathlib import Path
from typing import Any, Dict, List, Optional
import yaml
@@ -25,16 +25,7 @@ def _build_agent_configs() -> dict[str, Any]:
if key == "generic":
continue
if integration.registrar_config:
config = dict(integration.registrar_config)
# Propagate invoke_separator from the integration class when the
# registrar_config dict doesn't already declare it explicitly.
# SkillsIntegration subclasses (claude, codex, …) set
# invoke_separator="-" as a class attribute but omit it from
# registrar_config, so without this they would fall back to "."
# when register_commands() resolves __SPECKIT_COMMAND_*__ tokens.
if "invoke_separator" not in config:
config["invoke_separator"] = integration.invoke_separator
configs[key] = config
configs[key] = dict(integration.registrar_config)
return configs
@@ -428,7 +419,9 @@ class CommandRegistrar:
normalized = Path(os.path.normpath(candidate))
base_normalized = Path(os.path.normpath(base))
if not normalized.is_relative_to(base_normalized):
raise ValueError(f"Output path {candidate!r} escapes directory {base!r}")
raise ValueError(
f"Output path {candidate!r} escapes directory {base!r}"
)
def register_commands(
self,
@@ -478,10 +471,7 @@ class CommandRegistrar:
if frontmatter.get("strategy") == "wrap":
from .presets import _substitute_core_template
body, core_frontmatter = _substitute_core_template(
body, cmd_name, project_root, self
)
body, core_frontmatter = _substitute_core_template(body, cmd_name, project_root, self)
frontmatter = dict(frontmatter)
for key in ("scripts", "agent_scripts"):
if key not in frontmatter and key in core_frontmatter:
@@ -502,16 +492,6 @@ class CommandRegistrar:
body, "$ARGUMENTS", agent_config["args"]
)
# Resolve __SPECKIT_COMMAND_*__ tokens using the agent's invoke separator.
# The separator is sourced from agent_config (populated by _build_agent_configs,
# which propagates each integration's invoke_separator class attribute).
# Deferred import of IntegrationBase avoids a circular import at module load
# (base.py itself imports CommandRegistrar lazily).
from specify_cli.integrations.base import IntegrationBase # noqa: PLC0415
_sep = agent_config.get("invoke_separator", ".")
body = IntegrationBase.resolve_command_refs(body, _sep)
output_name = self._compute_output_name(agent_name, cmd_name, agent_config)
if agent_config["extension"] == "/SKILL.md":
@@ -525,22 +505,12 @@ class CommandRegistrar:
project_root,
)
elif agent_config["format"] == "markdown":
body = self.resolve_skill_placeholders(
agent_name, frontmatter, body, project_root
)
body = self._convert_argument_placeholder(
body, "$ARGUMENTS", agent_config["args"]
)
output = self.render_markdown_command(
frontmatter, body, source_id, context_note
)
body = self.resolve_skill_placeholders(agent_name, frontmatter, body, project_root)
body = self._convert_argument_placeholder(body, "$ARGUMENTS", agent_config["args"])
output = self.render_markdown_command(frontmatter, body, source_id, context_note)
elif agent_config["format"] == "toml":
body = self.resolve_skill_placeholders(
agent_name, frontmatter, body, project_root
)
body = self._convert_argument_placeholder(
body, "$ARGUMENTS", agent_config["args"]
)
body = self.resolve_skill_placeholders(agent_name, frontmatter, body, project_root)
body = self._convert_argument_placeholder(body, "$ARGUMENTS", agent_config["args"])
output = self.render_toml_command(frontmatter, body, source_id)
elif agent_config["format"] == "yaml":
output = self.render_yaml_command(
@@ -715,11 +685,8 @@ class CommandRegistrar:
if agent_dir.exists():
try:
registered = self.register_commands(
agent_name,
commands,
source_id,
source_dir,
project_root,
agent_name, commands, source_id,
source_dir, project_root,
context_note=context_note,
)
if registered:

View File

@@ -66,7 +66,6 @@ def _register_builtins() -> None:
from .kilocode import KilocodeIntegration
from .kimi import KimiIntegration
from .kiro_cli import KiroCliIntegration
from .lingma import LingmaIntegration
from .opencode import OpencodeIntegration
from .pi import PiIntegration
from .qodercli import QodercliIntegration
@@ -98,7 +97,6 @@ def _register_builtins() -> None:
_register(KilocodeIntegration())
_register(KimiIntegration())
_register(KiroCliIntegration())
_register(LingmaIntegration())
_register(OpencodeIntegration())
_register(PiIntegration())
_register(QodercliIntegration())

View File

@@ -20,8 +20,6 @@ from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Any
import yaml
if TYPE_CHECKING:
from .manifest import IntegrationManifest
@@ -608,7 +606,6 @@ class IntegrationBase(ABC):
# For .mdc files, treat Speckit-generated frontmatter-only content as empty
if ctx_path.suffix == ".mdc":
import re
# Delete the file if only YAML frontmatter remains (no body content)
frontmatter_only = re.match(
r"^---\n.*?\n---\s*$", normalized, re.DOTALL
@@ -956,6 +953,7 @@ class TomlIntegration(IntegrationBase):
and ``>``) keep their YAML semantics instead of being treated as
raw text.
"""
import yaml
frontmatter_text, _ = TomlIntegration._split_frontmatter(content)
if not frontmatter_text:
@@ -1142,6 +1140,7 @@ class YamlIntegration(IntegrationBase):
@staticmethod
def _extract_frontmatter(content: str) -> dict[str, Any]:
"""Extract frontmatter as a dict from YAML frontmatter block."""
import yaml
if not content.startswith("---"):
return {}
@@ -1202,38 +1201,24 @@ class YamlIntegration(IntegrationBase):
text = text[len("speckit.") :]
return text.replace(".", " ").replace("-", " ").replace("_", " ").title()
@classmethod
def _build_yaml_header(cls, title: str, description: str) -> dict[str, Any]:
"""Build the base YAML header."""
header = {
"version": "1.0.0",
"title": title,
"description": description,
"author": {"contact": "spec-kit"},
"parameters": [
{
"key": "args",
"input_type": "string",
"requirement": "optional",
"default": "",
"description": "User input passed to the command.",
}
],
"extensions": [{"type": "builtin", "name": "developer"}],
"activities": ["Spec-Driven Development"],
}
return header
@classmethod
def _render_yaml(cls, title: str, description: str, body: str, source_id: str) -> str:
@staticmethod
def _render_yaml(title: str, description: str, body: str, source_id: str) -> str:
"""Render a YAML recipe file from title, description, and body.
Produces a Goose-compatible recipe with a literal block scalar
for the prompt content. Uses ``yaml.safe_dump()`` for the
header fields to ensure proper escaping.
"""
header = cls._build_yaml_header(title, description)
import yaml
header = {
"version": "1.0.0",
"title": title,
"description": description,
"author": {"contact": "spec-kit"},
"extensions": [{"type": "builtin", "name": "developer"}],
"activities": ["Spec-Driven Development"],
}
header_yaml = yaml.safe_dump(
header,
@@ -1242,20 +1227,12 @@ class YamlIntegration(IntegrationBase):
default_flow_style=False,
).strip()
# Indent the body for YAML block scalar
# Indent each line for YAML block scalar
indented = "\n".join(f" {line}" for line in body.split("\n"))
lines = [
header_yaml,
"prompt: |",
indented,
"",
f"# Source: {source_id}",
]
lines = [header_yaml, "prompt: |", indented, "", f"# Source: {source_id}"]
return "\n".join(lines) + "\n"
def setup(
self,
project_root: Path,
@@ -1414,6 +1391,7 @@ class SkillsIntegration(IntegrationBase):
template. Each SKILL.md has normalised frontmatter containing
``name``, ``description``, ``compatibility``, and ``metadata``.
"""
import yaml
templates = self.list_command_templates()
if not templates:

View File

@@ -87,10 +87,8 @@ class ForgeIntegration(MarkdownIntegration):
"strip_frontmatter_keys": ["handoffs"],
"inject_name": True,
"format_name": format_forge_command_name, # Custom name formatter
"invoke_separator": "-",
}
context_file = "AGENTS.md"
invoke_separator = "-"
def setup(
self,
@@ -135,7 +133,6 @@ class ForgeIntegration(MarkdownIntegration):
processed = self.process_template(
raw, self.key, script_type, arg_placeholder,
context_file=self.context_file or "",
invoke_separator=self.invoke_separator,
)
# FORGE-SPECIFIC: Ensure any remaining $ARGUMENTS placeholders are

View File

@@ -1,41 +0,0 @@
"""Lingma IDE integration. — skills-based agent.
Lingma IDE uses ``.lingma/skills/speckit-<name>/SKILL.md`` layout.
In Specify CLI, the Lingma integration is skills-only, and ``--skills``
defaults to ``True``.
"""
from __future__ import annotations
from ..base import IntegrationOption, SkillsIntegration
class LingmaIntegration(SkillsIntegration):
"""Integration for Lingma IDE."""
key = "lingma"
config = {
"name": "Lingma",
"folder": ".lingma/",
"commands_subdir": "skills",
"install_url": None,
"requires_cli": False,
}
registrar_config = {
"dir": ".lingma/skills",
"format": "markdown",
"args": "$ARGUMENTS",
"extension": "/SKILL.md",
}
context_file = ".lingma/rules/specify-rules.md"
@classmethod
def options(cls) -> list[IntegrationOption]:
return [
IntegrationOption(
"--skills",
is_flag=True,
default=True,
help="Install as agent skills",
),
]

View File

@@ -88,7 +88,6 @@ You **MUST** consider the user input before proceeding (if not empty).
- **IF EXISTS**: Read data-model.md for entities and relationships
- **IF EXISTS**: Read contracts/ for API specifications and test requirements
- **IF EXISTS**: Read research.md for technical decisions and constraints
- **IF EXISTS**: Read /memory/constitution.md for governance constraints
- **IF EXISTS**: Read quickstart.md for integration scenarios
4. **Project Setup Verification**:

View File

@@ -81,29 +81,6 @@ class TestInitIntegrationFlag:
shared_manifest = project / ".specify" / "integrations" / "speckit.manifest.json"
assert shared_manifest.exists()
def test_noninteractive_init_defaults_to_copilot(self, tmp_path, monkeypatch):
from typer.testing import CliRunner
from specify_cli import app
import specify_cli
def fail_select(*_args, **_kwargs):
raise AssertionError("non-interactive init should not open the integration picker")
monkeypatch.setattr(specify_cli, "select_with_arrows", fail_select)
runner = CliRunner()
project = tmp_path / "noninteractive"
result = runner.invoke(app, [
"init", str(project), "--script", "sh", "--no-git", "--ignore-agent-tools",
], catch_exceptions=False)
assert result.exit_code == 0, result.output
assert f"defaulting to '{specify_cli.DEFAULT_INIT_INTEGRATION}'" in result.output
assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists()
data = json.loads((project / ".specify" / "integration.json").read_text(encoding="utf-8"))
assert data["integration"] == specify_cli.DEFAULT_INIT_INTEGRATION
def test_ai_copilot_auto_promotes(self, tmp_path):
from typer.testing import CliRunner
from specify_cli import app
@@ -1140,7 +1117,6 @@ class TestIntegrationCatalogDiscoveryCLI:
["workflow", "remove", "demo"],
["workflow", "search"],
["workflow", "info", "demo"],
["workflow", "catalog", "list"],
["workflow", "catalog", "add", "https://example.com/catalog.yml"],
["workflow", "catalog", "remove", "0"],
]

View File

@@ -196,10 +196,7 @@ class TestClaudeIntegration:
try:
os.chdir(project)
runner = CliRunner()
with (
patch("specify_cli._stdin_is_interactive", return_value=True),
patch("specify_cli.select_with_arrows", return_value="claude"),
):
with patch("specify_cli.select_with_arrows", return_value="claude"):
result = runner.invoke(
app,
[

View File

@@ -141,7 +141,6 @@ class TestForgeIntegration:
assert actual_commands == expected_commands
def test_templates_are_processed(self, tmp_path):
import re
from specify_cli.integrations.forge import ForgeIntegration
forge = ForgeIntegration()
m = IntegrationManifest("forge", tmp_path)
@@ -158,11 +157,6 @@ class TestForgeIntegration:
assert "$ARGUMENTS" not in content, f"{cmd_file.name} has unprocessed $ARGUMENTS"
# Frontmatter sections should be stripped
assert "\nscripts:\n" not in content
# Check Forge-specific: command references use hyphen notation, not dot notation
assert not re.search(r"/speckit\.[a-z]", content), (
f"{cmd_file.name} contains dot-notation command reference (/speckit.<cmd>); "
"Forge requires hyphen notation (/speckit-<cmd>) for ZSH compatibility"
)
def test_plan_references_correct_context_file(self, tmp_path):
"""The generated plan command must reference forge's context file."""
@@ -230,33 +224,6 @@ class TestForgeIntegration:
"checklist should contain {{parameters}} in User Input section"
)
def test_command_refs_use_hyphen_notation(self, tmp_path):
"""Verify all generated Forge command files use /speckit-foo, not /speckit.foo."""
import re
from specify_cli.integrations.forge import ForgeIntegration
forge = ForgeIntegration()
m = IntegrationManifest("forge", tmp_path)
forge.setup(tmp_path, m)
commands_dir = tmp_path / ".forge" / "commands"
files_with_refs = []
files_with_dot_refs = []
for cmd_file in commands_dir.glob("speckit.*.md"):
content = cmd_file.read_text(encoding="utf-8")
if re.search(r"/speckit-[a-z]", content):
files_with_refs.append(cmd_file.name)
if re.search(r"/speckit\.[a-z]", content):
files_with_dot_refs.append(cmd_file.name)
assert files_with_dot_refs == [], (
f"Files contain dot-notation command references: {files_with_dot_refs}. "
"Forge requires hyphen notation (/speckit-<cmd>) for ZSH compatibility."
)
assert len(files_with_refs) > 0, (
"Expected at least one generated Forge command to contain /speckit-<cmd> reference, "
"but none were found. Check that __SPECKIT_COMMAND_*__ tokens are being resolved."
)
def test_name_field_uses_hyphenated_format(self, tmp_path):
"""Verify that injected name fields use hyphenated format (speckit-plan, not speckit.plan)."""
from specify_cli.integrations.forge import ForgeIntegration
@@ -434,48 +401,3 @@ class TestForgeCommandRegistrar:
assert "name:" not in content, (
"Windsurf should not inject name field - format_name callback should be Forge-only"
)
def test_git_extension_command_uses_hyphen_notation(self, tmp_path):
"""Verify the git extension's feature command uses /speckit-specify (not /speckit.specify) for Forge."""
from pathlib import Path
from specify_cli.agents import CommandRegistrar
# Locate the real git extension command source file
repo_root = Path(__file__).resolve().parent.parent.parent
ext_dir = repo_root / "extensions" / "git"
cmd_source = ext_dir / "commands" / "speckit.git.feature.md"
assert cmd_source.exists(), (
f"Git extension command source not found at {cmd_source}. "
"Ensure extensions/git/commands/speckit.git.feature.md exists."
)
registrar = CommandRegistrar()
commands = [
{
"name": "speckit.git.feature",
"file": "commands/speckit.git.feature.md",
}
]
registered = registrar.register_commands(
"forge",
commands,
"git",
ext_dir,
tmp_path,
)
assert "speckit.git.feature" in registered
forge_cmd = tmp_path / ".forge" / "commands" / "speckit.git.feature.md"
assert forge_cmd.exists(), "Expected Forge command file was not created"
content = forge_cmd.read_text(encoding="utf-8")
assert "/speckit-specify" in content, (
"Expected '/speckit-specify' (hyphen) in generated Forge git.feature command body, "
"but it was not found. Check that __SPECKIT_COMMAND_SPECIFY__ is resolved correctly."
)
assert "/speckit.specify" not in content, (
"Found '/speckit.specify' (dot notation) in generated Forge git.feature command body. "
"Forge requires hyphen notation for ZSH compatibility."
)

View File

@@ -185,16 +185,6 @@ class TestGenericIntegration:
)
assert "__CONTEXT_FILE__" not in content
def test_implement_loads_constitution_context(self, tmp_path):
"""The generated implement command should load constitution governance context."""
i = get_integration("generic")
m = IntegrationManifest("generic", tmp_path)
i.setup(tmp_path, m, parsed_options={"commands_dir": ".custom/cmds"})
implement_file = tmp_path / ".custom" / "cmds" / "speckit.implement.md"
assert implement_file.exists()
content = implement_file.read_text(encoding="utf-8")
assert ".specify/memory/constitution.md" in content
# -- CLI --------------------------------------------------------------
def test_cli_generic_without_commands_dir_fails(self, tmp_path):

View File

@@ -1,9 +1,5 @@
"""Tests for GooseIntegration."""
import yaml
from specify_cli.integrations import get_integration
from specify_cli.integrations.manifest import IntegrationManifest
from .test_integration_base_yaml import YamlIntegrationTests
@@ -13,27 +9,3 @@ class TestGooseIntegration(YamlIntegrationTests):
COMMANDS_SUBDIR = "recipes"
REGISTRAR_DIR = ".goose/recipes"
CONTEXT_FILE = "AGENTS.md"
def test_setup_declares_args_parameter_for_args_prompt(self, tmp_path):
# “If a generated Goose recipe uses {{args}} in its prompt, it
# must declare a corresponding args parameter.”
integration = get_integration("goose")
assert integration is not None
manifest = IntegrationManifest("goose", tmp_path)
created = integration.setup(tmp_path, manifest, script_type="sh")
recipe_files = [path for path in created if path.suffix == ".yaml"]
assert recipe_files
for recipe_file in recipe_files:
data = yaml.safe_load(recipe_file.read_text(encoding="utf-8"))
if "{{args}}" not in data["prompt"]:
continue
assert any(
param.get("key") == "args"
for param in data.get("parameters", [])
), f"{recipe_file} uses {{{{args}}}} but does not declare args"

View File

@@ -1,11 +0,0 @@
"""Tests for LingmaIntegration."""
from .test_integration_base_skills import SkillsIntegrationTests
class TestLingmaIntegration(SkillsIntegrationTests):
KEY = "lingma"
FOLDER = ".lingma/"
COMMANDS_SUBDIR = "skills"
REGISTRAR_DIR = ".lingma/skills"
CONTEXT_FILE = ".lingma/rules/specify-rules.md"

View File

@@ -5,6 +5,7 @@ from pathlib import Path
from specify_cli import AGENT_CONFIG, AI_ASSISTANT_ALIASES, AI_ASSISTANT_HELP
from specify_cli.extensions import CommandRegistrar
REPO_ROOT = Path(__file__).resolve().parent.parent
@@ -198,88 +199,3 @@ class TestAgentConfigConsistency:
def test_ai_help_includes_goose(self):
"""CLI help text for --ai should include goose."""
assert "goose" in AI_ASSISTANT_HELP
# --- invoke_separator propagation checks ---
def test_skills_agents_have_hyphen_invoke_separator_in_agent_configs(self):
"""Skills-based agents must expose invoke_separator='-' in AGENT_CONFIGS.
SkillsIntegration sets ``invoke_separator = "-"`` as a class attribute,
but individual skills integrations (claude, codex, …) do not repeat it in
their ``registrar_config`` dicts. ``_build_agent_configs()`` must
propagate the class attribute so that ``register_commands()`` resolves
``__SPECKIT_COMMAND_*__`` tokens with the correct hyphen separator.
"""
cfg = CommandRegistrar.AGENT_CONFIGS
skills_agents = [
key for key, c in cfg.items() if c.get("extension") == "/SKILL.md"
]
assert skills_agents, (
"Expected at least one skills-based agent in AGENT_CONFIGS"
)
for agent in skills_agents:
assert cfg[agent].get("invoke_separator") == "-", (
f"Skills agent '{agent}' has invoke_separator="
f"{cfg[agent].get('invoke_separator')!r} in AGENT_CONFIGS; "
"expected '-' (propagated from SkillsIntegration.invoke_separator)"
)
def test_skills_agent_command_token_resolves_with_hyphen(self, tmp_path):
"""__SPECKIT_COMMAND_*__ tokens in extension commands resolve to /speckit-<cmd>
when registered for a skills-based agent (e.g. claude).
Regression guard: before the fix, _build_agent_configs() did not
propagate invoke_separator from the integration class, so
register_commands() fell back to '.' and emitted /speckit.specify instead
of /speckit-specify for skills agents.
"""
import re
from pathlib import Path
from specify_cli.agents import CommandRegistrar
repo_root = Path(__file__).resolve().parent.parent
ext_dir = repo_root / "extensions" / "git"
cmd_source = ext_dir / "commands" / "speckit.git.feature.md"
assert cmd_source.exists(), (
f"Git extension command source not found at {cmd_source}"
)
assert "__SPECKIT_COMMAND_SPECIFY__" in cmd_source.read_text(
encoding="utf-8"
), (
"Expected __SPECKIT_COMMAND_SPECIFY__ token in speckit.git.feature.md; "
"check that the file uses the token rather than a hard-coded ref."
)
registrar = CommandRegistrar()
commands = [
{"name": "speckit.git.feature", "file": "commands/speckit.git.feature.md"}
]
registered = registrar.register_commands(
"claude",
commands,
"git",
ext_dir,
tmp_path,
)
assert "speckit.git.feature" in registered
skill_file = (
tmp_path / ".claude" / "skills" / "speckit-git-feature" / "SKILL.md"
)
assert skill_file.exists(), (
f"Expected Claude skill file not found at {skill_file}"
)
content = skill_file.read_text(encoding="utf-8")
assert "/speckit-specify" in content, (
"Expected '/speckit-specify' (hyphen) in generated Claude skill for git.feature; "
"__SPECKIT_COMMAND_SPECIFY__ was not resolved with the correct separator."
)
# Negative lookbehind (?<![a-zA-Z0-9_]) excludes file-path occurrences
# such as 'source: git:commands/speckit.git.feature.md' in frontmatter,
# where the '/' is a path separator preceded by a word character.
assert not re.search(r"(?<![a-zA-Z0-9_])/speckit\.[a-z]", content), (
"Found dot-notation command ref (/speckit.<cmd>) in generated Claude skill. "
"Skills agents must use hyphen notation."
)

View File

@@ -1,79 +0,0 @@
"""Tests for GitHub-authenticated HTTP request helpers."""
import os
from unittest.mock import patch
import pytest
from specify_cli._github_http import (
build_github_request,
)
class TestBuildGitHubRequest:
"""Tests for build_github_request() URL validation and auth handling."""
# --- URL Validation Tests ---
def test_empty_url_raises_value_error(self):
"""build_github_request() must reject an empty string URL."""
with pytest.raises(ValueError, match="url must not be empty"):
build_github_request("")
def test_whitespace_url_raises_value_error(self):
"""build_github_request() must reject a whitespace-only URL."""
with pytest.raises(ValueError, match="url must not be empty"):
build_github_request(" ")
def test_non_http_url_raises_value_error(self):
"""build_github_request() must reject URLs without http/https scheme."""
with pytest.raises(ValueError, match="url must start with http"):
build_github_request("not-a-url")
def test_ftp_url_raises_value_error(self):
"""build_github_request() must reject ftp:// URLs."""
with pytest.raises(ValueError, match="url must start with http"):
build_github_request("ftp://github.com/file.zip")
# --- Valid URL Tests ---
def test_valid_https_url_returns_request(self):
"""build_github_request() must return a Request for a valid https URL."""
req = build_github_request("https://github.com/github/spec-kit")
assert req.full_url == "https://github.com/github/spec-kit"
def test_valid_http_url_returns_request(self):
"""build_github_request() must accept http:// URLs."""
req = build_github_request("http://example.com/file")
assert req.full_url == "http://example.com/file"
# --- Auth Header Tests ---
def test_github_token_added_for_github_host(self):
"""Authorization header is set when GITHUB_TOKEN is present."""
with patch.dict(os.environ, {"GITHUB_TOKEN": "test-token", "GH_TOKEN": ""}):
req = build_github_request("https://github.com/github/spec-kit")
assert req.get_header("Authorization") == "Bearer test-token"
def test_gh_token_used_as_fallback(self):
"""GH_TOKEN is used when GITHUB_TOKEN is absent."""
with patch.dict(os.environ, {"GITHUB_TOKEN": "", "GH_TOKEN": "fallback-token"}):
req = build_github_request("https://github.com/github/spec-kit")
assert req.get_header("Authorization") == "Bearer fallback-token"
def test_no_auth_header_for_non_github_host(self):
"""Authorization header must NOT be set for non-GitHub URLs."""
with patch.dict(os.environ, {"GITHUB_TOKEN": "test-token"}):
req = build_github_request("https://example.com/file")
assert req.get_header("Authorization") is None
def test_no_auth_header_when_no_token(self):
"""No Authorization header when no token is set in environment."""
with patch.dict(os.environ, {}, clear=True):
req = build_github_request("https://github.com/github/spec-kit")
assert req.get_header("Authorization") is None
def test_missing_hostname_raises_value_error(self):
"""build_github_request() must reject URLs with valid scheme but no hostname."""
with pytest.raises(ValueError, match="url must include a hostname"):
build_github_request("http://")