mirror of
https://github.com/github/spec-kit.git
synced 2026-07-07 22:56:57 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44de9235a8 | ||
|
|
efdff310a2 | ||
|
|
372b22a9bc | ||
|
|
765e60f1c4 | ||
|
|
92186124f3 | ||
|
|
20ef9a72a9 | ||
|
|
cba00ab9a5 | ||
|
|
a7f6800fcc | ||
|
|
cd951acb9e | ||
|
|
756d632129 | ||
|
|
0593565607 | ||
|
|
bf47e89249 | ||
|
|
81f772c60b |
35
CHANGELOG.md
35
CHANGELOG.md
@@ -2,6 +2,41 @@
|
|||||||
|
|
||||||
<!-- insert new changelog below this comment -->
|
<!-- insert new changelog below this comment -->
|
||||||
|
|
||||||
|
## [0.8.9] - 2026-05-12
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- docs: revamp landing page with four-pillar card layout (#2531)
|
||||||
|
- feat(extensions): update governance ecosystem extensions to latest versions (#2514)
|
||||||
|
- Add changelog extension (#2177)
|
||||||
|
- Add install directory to docfx.json file references (#2522)
|
||||||
|
- feat(catalog): add BrownKit (brownkit) community extension (#2510) (#2520)
|
||||||
|
- fix(kiro-cli): replace literal $ARGUMENTS with prose fallback (#2482)
|
||||||
|
- Preset: Add game-narrative-writing preset to community catalog (#2454)
|
||||||
|
- docs: clarify CLI upgrade discovery (#2519)
|
||||||
|
- fix: make template metadata line breaks markdownlint-safe (#2505)
|
||||||
|
- refactor(catalogs): extract integration catalog config loading (#2497)
|
||||||
|
- test(presets): silence expected UserWarnings in self-test composition… (#2373)
|
||||||
|
- chore: release 0.8.8, begin 0.8.9.dev0 development (#2516)
|
||||||
|
|
||||||
|
## [0.8.8] - 2026-05-11
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- chore(deps): bump actions/checkout from 4.3.1 to 6.0.2 (#2486)
|
||||||
|
- feat(catalog): add Spec Kit Schedule (schedule) community extension (#2473)
|
||||||
|
- fix(integration): refresh shared infra on `integration switch` (#2375)
|
||||||
|
- Add MDE preset to community catalog (#2513)
|
||||||
|
- Add MDE extension to community catalog (#2512)
|
||||||
|
- chore: update community catalog with latest extension versions (#2490)
|
||||||
|
- chore(deps): bump actions/setup-dotnet from 4.3.1 to 5.2.0 (#2489)
|
||||||
|
- chore(deps): bump actions/github-script from 7 to 9 (#2488)
|
||||||
|
- chore(deps): bump DavidAnson/markdownlint-cli2-action (#2487)
|
||||||
|
- chore(deps): bump github/codeql-action from 4.35.3 to 4.35.4 (#2485)
|
||||||
|
- feat(catalog): add API Evolve (api-evolve) community extension (#2479)
|
||||||
|
- feat: Config-driven opt-in authentication registry with multi-platform support (#2393)
|
||||||
|
- chore: release 0.8.7, begin 0.8.8.dev0 development (#2480)
|
||||||
|
|
||||||
## [0.8.7] - 2026-05-07
|
## [0.8.7] - 2026-05-07
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ The following community-contributed extensions are available in [`catalog.commun
|
|||||||
| 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) |
|
| 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) |
|
||||||
| Branch Convention | Configurable branch and folder naming conventions for /specify with presets and custom patterns | `process` | Read+Write | [spec-kit-branch-convention](https://github.com/Quratulain-bilal/spec-kit-branch-convention) |
|
| Branch Convention | Configurable branch and folder naming conventions for /specify with presets and custom patterns | `process` | Read+Write | [spec-kit-branch-convention](https://github.com/Quratulain-bilal/spec-kit-branch-convention) |
|
||||||
| Brownfield Bootstrap | Bootstrap spec-kit for existing codebases — auto-discover architecture and adopt SDD incrementally | `process` | Read+Write | [spec-kit-brownfield](https://github.com/Quratulain-bilal/spec-kit-brownfield) |
|
| Brownfield Bootstrap | Bootstrap spec-kit for existing codebases — auto-discover architecture and adopt SDD incrementally | `process` | Read+Write | [spec-kit-brownfield](https://github.com/Quratulain-bilal/spec-kit-brownfield) |
|
||||||
|
| BrownKit | Evidence-driven capability discovery, security and QA risk assessment for existing codebases | `process` | Read+Write | [BrownKit](https://github.com/MaksimShevtsov/BrownKit) |
|
||||||
| Bugfix Workflow | Structured bugfix workflow — capture bugs, trace to spec artifacts, and patch specs surgically | `process` | Read+Write | [spec-kit-bugfix](https://github.com/Quratulain-bilal/spec-kit-bugfix) |
|
| Bugfix Workflow | Structured bugfix workflow — capture bugs, trace to spec artifacts, and patch specs surgically | `process` | Read+Write | [spec-kit-bugfix](https://github.com/Quratulain-bilal/spec-kit-bugfix) |
|
||||||
| Canon | Adds canon-driven (baseline-driven) workflows: spec-first, code-first, spec-drift. Requires Canon Core preset installation. | `process` | Read+Write | [spec-kit-canon](https://github.com/maximiliamus/spec-kit-canon/tree/master/extension) |
|
| Canon | Adds canon-driven (baseline-driven) workflows: spec-first, code-first, spec-drift. Requires Canon Core preset installation. | `process` | Read+Write | [spec-kit-canon](https://github.com/maximiliamus/spec-kit-canon/tree/master/extension) |
|
||||||
| Catalog CI | Automated validation for spec-kit community catalog entries — structure, URLs, diffs, and linting | `process` | Read-only | [spec-kit-catalog-ci](https://github.com/Quratulain-bilal/spec-kit-catalog-ci) |
|
| Catalog CI | Automated validation for spec-kit community catalog entries — structure, URLs, diffs, and linting | `process` | Read-only | [spec-kit-catalog-ci](https://github.com/Quratulain-bilal/spec-kit-catalog-ci) |
|
||||||
@@ -265,11 +266,12 @@ The following community-contributed extensions are available in [`catalog.commun
|
|||||||
| Security Review | Full-project secure-by-design security audits plus staged, branch/PR, plan, task, follow-up, and apply reviews | `code` | Read+Write | [spec-kit-security-review](https://github.com/DyanGalih/spec-kit-security-review) |
|
| Security Review | Full-project secure-by-design security audits plus staged, branch/PR, plan, task, follow-up, and apply reviews | `code` | Read+Write | [spec-kit-security-review](https://github.com/DyanGalih/spec-kit-security-review) |
|
||||||
| SFSpeckit | Enterprise Salesforce SDLC with 18 commands for the full SDD lifecycle. | `process` | Read+Write | [spec-kit-sf](https://github.com/ysumanth06/spec-kit-sf) |
|
| SFSpeckit | Enterprise Salesforce SDLC with 18 commands for the full SDD lifecycle. | `process` | Read+Write | [spec-kit-sf](https://github.com/ysumanth06/spec-kit-sf) |
|
||||||
| Ship Release Extension | Automates release pipeline: pre-flight checks, branch sync, changelog generation, CI verification, and PR creation | `process` | Read+Write | [spec-kit-ship](https://github.com/arunt14/spec-kit-ship) |
|
| Ship Release Extension | Automates release pipeline: pre-flight checks, branch sync, changelog generation, CI verification, and PR creation | `process` | Read+Write | [spec-kit-ship](https://github.com/arunt14/spec-kit-ship) |
|
||||||
| Spec Reference Loader | Reads the ## References section from the feature spec and loads only the listed docs into context | `docs` | Read-only | [spec-kit-spec-reference-loader](https://github.com/KevinBrown5280/spec-kit-spec-reference-loader) |
|
| Spec Changelog | Auto-generate changelogs and release notes from spec git history and requirement diffs | `docs` | Read-only | [spec-kit-changelog](https://github.com/Quratulain-bilal/spec-kit-changelog) |
|
||||||
| Spec Critique Extension | Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives | `docs` | Read-only | [spec-kit-critique](https://github.com/arunt14/spec-kit-critique) |
|
| Spec Critique Extension | Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives | `docs` | Read-only | [spec-kit-critique](https://github.com/arunt14/spec-kit-critique) |
|
||||||
| Spec Diagram | Auto-generate Mermaid diagrams of SDD workflow state, feature progress, and task dependencies | `visibility` | Read-only | [spec-kit-diagram-](https://github.com/Quratulain-bilal/spec-kit-diagram-) |
|
| Spec Diagram | Auto-generate Mermaid diagrams of SDD workflow state, feature progress, and task dependencies | `visibility` | Read-only | [spec-kit-diagram-](https://github.com/Quratulain-bilal/spec-kit-diagram-) |
|
||||||
| Spec Kit Schedule | Optimal multi-agent task scheduling via CP-SAT — DAG precedence, hallucination-aware caps, file-conflict avoidance, stochastic durations, replanning, and interactive HTML output | `process` | Read+Write | [spec-kit-schedule](https://github.com/jfranc38/spec-kit-schedule) |
|
| Spec Kit Schedule | Optimal multi-agent task scheduling via CP-SAT — DAG precedence, hallucination-aware caps, file-conflict avoidance, stochastic durations, replanning, and interactive HTML output | `process` | Read+Write | [spec-kit-schedule](https://github.com/jfranc38/spec-kit-schedule) |
|
||||||
| Spec Orchestrator | Cross-feature orchestration — track state, select tasks, and detect conflicts across parallel specs | `process` | Read-only | [spec-kit-orchestrator](https://github.com/Quratulain-bilal/spec-kit-orchestrator) |
|
| Spec Orchestrator | Cross-feature orchestration — track state, select tasks, and detect conflicts across parallel specs | `process` | Read-only | [spec-kit-orchestrator](https://github.com/Quratulain-bilal/spec-kit-orchestrator) |
|
||||||
|
| Spec Reference Loader | Reads the ## References section from the feature spec and loads only the listed docs into context | `docs` | Read-only | [spec-kit-spec-reference-loader](https://github.com/KevinBrown5280/spec-kit-spec-reference-loader) |
|
||||||
| Spec Refine | Update specs in-place, propagate changes to plan and tasks, and diff impact across artifacts | `process` | Read+Write | [spec-kit-refine](https://github.com/Quratulain-bilal/spec-kit-refine) |
|
| Spec Refine | Update specs in-place, propagate changes to plan and tasks, and diff impact across artifacts | `process` | Read+Write | [spec-kit-refine](https://github.com/Quratulain-bilal/spec-kit-refine) |
|
||||||
| Spec Scope | Effort estimation and scope tracking — estimate work, detect creep, and budget time per phase | `process` | Read-only | [spec-kit-scope-](https://github.com/Quratulain-bilal/spec-kit-scope-) |
|
| Spec Scope | Effort estimation and scope tracking — estimate work, detect creep, and budget time per phase | `process` | Read-only | [spec-kit-scope-](https://github.com/Quratulain-bilal/spec-kit-scope-) |
|
||||||
| Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | `docs` | Read+Write | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) |
|
| Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | `docs` | Read+Write | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) |
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ The following community-contributed presets customize how Spec Kit behaves — o
|
|||||||
| Cross-Platform Governance | Adds Bash/PowerShell parity, dry-run/WhatIf parity, Unix man-page expectations, PowerShell comment-based help, and Verb-Noun Cmdlet discipline | 8 templates, 3 commands | — | [spec-kit-preset-cross-platform-governance](https://github.com/hindermath/spec-kit-preset-cross-platform-governance) |
|
| Cross-Platform Governance | Adds Bash/PowerShell parity, dry-run/WhatIf parity, Unix man-page expectations, PowerShell comment-based help, and Verb-Noun Cmdlet discipline | 8 templates, 3 commands | — | [spec-kit-preset-cross-platform-governance](https://github.com/hindermath/spec-kit-preset-cross-platform-governance) |
|
||||||
| Explicit Task Dependencies | Adds explicit `(depends on T###)` dependency declarations and an Execution Wave DAG to tasks.md for parallel scheduling | 1 template, 1 command | — | [spec-kit-preset-explicit-task-dependencies](https://github.com/Quratulain-bilal/spec-kit-preset-explicit-task-dependencies) |
|
| Explicit Task Dependencies | Adds explicit `(depends on T###)` dependency declarations and an Execution Wave DAG to tasks.md for parallel scheduling | 1 template, 1 command | — | [spec-kit-preset-explicit-task-dependencies](https://github.com/Quratulain-bilal/spec-kit-preset-explicit-task-dependencies) |
|
||||||
| Fiction Book Writing | It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose. Supports interactive elements like brainstorming, interview, roleplay and extras like statistics, cover builder and bio command. Export with templates for KDP, D2D etc. | 22 templates, 27 commands, 2 scripts | — | [speckit-preset-fiction-book-writing](https://github.com/adaumann/speckit-preset-fiction-book-writing) |
|
| Fiction Book Writing | It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose. Supports interactive elements like brainstorming, interview, roleplay and extras like statistics, cover builder and bio command. Export with templates for KDP, D2D etc. | 22 templates, 27 commands, 2 scripts | — | [speckit-preset-fiction-book-writing](https://github.com/adaumann/speckit-preset-fiction-book-writing) |
|
||||||
|
| Game Narrative Writing | Spec-Driven Development for interactive game narrative pre-production for video games. Authors write in a portable generic format, Twine/Sugarcube (.twee) or Ink (.ink). Covers choice-IF, visual novels, and branching dialogue. Supports Tier 1 mechanic hooks (flag, counter, inventory, timer, trust, currency, npc_state, ending_condition), multi-ending design, series carry-over variable registry, and NPC-focused character architecture. | 22 templates, 36 commands, 2 scripts | — | [speckit-preset-game-narrative-writing](https://github.com/adaumann/speckit-preset-game-narrative-writing) |
|
||||||
| iSAQB Architecture Governance | Adds general iSAQB/CPSA-F and arc42 architecture governance: goals, context, building blocks, runtime and deployment views, quality scenarios, ADRs, risks, and technical debt | 13 templates, 3 commands | — | [spec-kit-preset-isaqb-architecture-governance](https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance) |
|
| iSAQB Architecture Governance | Adds general iSAQB/CPSA-F and arc42 architecture governance: goals, context, building blocks, runtime and deployment views, quality scenarios, ADRs, risks, and technical debt | 13 templates, 3 commands | — | [spec-kit-preset-isaqb-architecture-governance](https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance) |
|
||||||
| Jira Issue Tracking | Overrides `speckit.taskstoissues` to create Jira epics, stories, and tasks instead of GitHub Issues via Atlassian MCP tools | 1 command | — | [spec-kit-preset-jira](https://github.com/luno/spec-kit-preset-jira) |
|
| Jira Issue Tracking | Overrides `speckit.taskstoissues` to create Jira epics, stories, and tasks instead of GitHub Issues via Atlassian MCP tools | 1 command | — | [spec-kit-preset-jira](https://github.com/luno/spec-kit-preset-jira) |
|
||||||
| Model Driven Engineering | Focuses on streamlined commands, app repository support, cross-spec support, and capability-aware project memory for model-driven engineering workflows | 6 templates, 11 commands | MDE extension | [spec-kit-preset-mde](https://github.com/AI-MDE/spec-kit-preset-mde) |
|
| Model Driven Engineering | Focuses on streamlined commands, app repository support, cross-spec support, and capability-aware project memory for model-driven engineering workflows | 6 templates, 11 commands | MDE extension | [spec-kit-preset-mde](https://github.com/AI-MDE/spec-kit-preset-mde) |
|
||||||
|
|||||||
46
docs/concepts/sdd.md
Normal file
46
docs/concepts/sdd.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
@@ -6,7 +6,9 @@
|
|||||||
"*.md",
|
"*.md",
|
||||||
"toc.yml",
|
"toc.yml",
|
||||||
"community/*.md",
|
"community/*.md",
|
||||||
"reference/*.md"
|
"concepts/*.md",
|
||||||
|
"reference/*.md",
|
||||||
|
"install/*.md"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -49,7 +51,8 @@
|
|||||||
"fileMetadataFiles": [],
|
"fileMetadataFiles": [],
|
||||||
"template": [
|
"template": [
|
||||||
"default",
|
"default",
|
||||||
"modern"
|
"modern",
|
||||||
|
"template"
|
||||||
],
|
],
|
||||||
"postProcessors": [],
|
"postProcessors": [],
|
||||||
"markdownEngineName": "markdig",
|
"markdownEngineName": "markdig",
|
||||||
@@ -67,6 +70,11 @@
|
|||||||
"repo": "https://github.com/github/spec-kit",
|
"repo": "https://github.com/github/spec-kit",
|
||||||
"branch": "main"
|
"branch": "main"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"fileMetadata": {
|
||||||
|
"_layout": {
|
||||||
|
"index.md": "landing"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
169
docs/index.md
169
docs/index.md
@@ -1,67 +1,152 @@
|
|||||||
# Spec Kit
|
<div class="landing-hero">
|
||||||
|
|
||||||
*Build high-quality software faster.*
|
# GitHub Spec Kit
|
||||||
|
|
||||||
**An effort to allow organizations to focus on product scenarios rather than writing undifferentiated code with the help of Spec-Driven Development.**
|
**Define what to build before building it — with any AI coding agent.**
|
||||||
|
|
||||||
## What is Spec-Driven Development?
|
Spec Kit is a toolkit for [Spec-Driven Development](concepts/sdd.md) (SDD), a methodology that puts specifications at the center of AI-assisted software development. Instead of jumping straight to code, you describe *what* to build, refine it through structured phases, and let your AI coding agent implement it.
|
||||||
|
|
||||||
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.
|
<a href="installation.md" class="btn btn-primary btn-lg">Install Spec Kit</a>
|
||||||
|
<a href="quickstart.md" class="btn btn-outline-primary btn-lg">Quick Start</a>
|
||||||
|
|
||||||
## Getting Started
|
</div>
|
||||||
|
|
||||||
- [Installation Guide](installation.md)
|
---
|
||||||
- [Quick Start Guide](quickstart.md)
|
|
||||||
- [Upgrade Guide](upgrade.md)
|
|
||||||
- [Local Development](local-development.md)
|
|
||||||
|
|
||||||
## Core Philosophy
|
<div class="pillar-grid">
|
||||||
|
|
||||||
Spec-Driven Development is a structured process that emphasizes:
|
<div class="pillar-card">
|
||||||
|
|
||||||
- **Intent-driven development** where specifications define the "*what*" before the "*how*"
|
### Spec-driven by default
|
||||||
- **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
|
The core SDD process ships ready to use: **Spec → Plan → Tasks → Implement**.
|
||||||
|
|
||||||
| Phase | Focus | Key Activities |
|
Define what to build before building it. Rich templates, quality checklists, and cross-artifact analysis come out of the box. Each phase produces a Markdown artifact that feeds the next — giving your AI coding agent structured context instead of ad-hoc prompts.
|
||||||
|-------|-------|----------------|
|
|
||||||
| **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
|
<a href="quickstart.md" class="pillar-link">Walk through the workflow →</a>
|
||||||
|
|
||||||
Our research and experimentation focus on:
|
</div>
|
||||||
|
|
||||||
### Technology Independence
|
<div class="pillar-card">
|
||||||
|
|
||||||
- Create applications using diverse technology stacks
|
### Use any coding agent
|
||||||
- Validate the hypothesis that Spec-Driven Development is a process not tied to specific technologies, programming languages, or frameworks
|
|
||||||
|
|
||||||
### Enterprise Constraints
|
<span class="pillar-stat">30 integrations</span> — Copilot, Gemini, Codex, Windsurf, Claude, Forge, Kiro, and more. Switch freely between agents with a single command. No lock-in.
|
||||||
|
|
||||||
- Demonstrate mission-critical application development
|
Run `specify init` with your agent of choice and Spec Kit sets up the right command files, context rules, and directory structures automatically. If your agent isn't listed, the `generic` integration is an escape hatch for any tool.
|
||||||
- Incorporate organizational constraints (cloud providers, tech stacks, engineering practices)
|
|
||||||
- Support enterprise design systems and compliance requirements
|
|
||||||
|
|
||||||
### User-Centric Development
|
<a href="reference/integrations.md" class="pillar-link">See all integrations →</a>
|
||||||
|
|
||||||
- Build applications for different user cohorts and preferences
|
</div>
|
||||||
- Support various development approaches (from vibe-coding to AI-native development)
|
|
||||||
|
|
||||||
### Creative & Iterative Processes
|
<div class="pillar-card">
|
||||||
|
|
||||||
- Validate the concept of parallel implementation exploration
|
### Make it your own
|
||||||
- Provide robust iterative feature development workflows
|
|
||||||
- Extend processes to handle upgrades and modernization tasks
|
|
||||||
|
|
||||||
## Contributing
|
<span class="pillar-stat">91 community extensions</span> (50+ authors), <span class="pillar-stat">18 presets</span>, and growing — including entirely different SDD processes:
|
||||||
|
|
||||||
Please see our [Contributing Guide](https://github.com/github/spec-kit/blob/main/CONTRIBUTING.md) for information on how to contribute to this project.
|
- **AIDE** — 7-step AI-driven engineering lifecycle
|
||||||
|
- **Canon** — baseline-driven workflows (spec-first, code-first, spec-drift)
|
||||||
|
- **Product Forge** — product-management-oriented SDD
|
||||||
|
- **FX→.NET** — end-to-end .NET Framework migration across 7 phases
|
||||||
|
- **MAQA** — multi-agent orchestration with quality assurance gates
|
||||||
|
|
||||||
## Support
|
Tune the core process with presets, extend it with extensions, orchestrate it with workflows, or replace it entirely. Build and publish your own.
|
||||||
|
|
||||||
For support, please check our [Support Guide](https://github.com/github/spec-kit/blob/main/SUPPORT.md) or open an issue on GitHub.
|
<a href="community/presets.md" class="pillar-link">Browse community presets →</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pillar-card">
|
||||||
|
|
||||||
|
### Integrate into your organization
|
||||||
|
|
||||||
|
Works offline, behind firewalls, and on **Windows, macOS, and Linux**. Host your own extension and preset catalogs so your organization controls what gets installed.
|
||||||
|
|
||||||
|
Community extensions like CI Guard and Architecture Guard add compliance gates and governance that fit the way your team already works.
|
||||||
|
|
||||||
|
<a href="installation.md" class="pillar-link">Installation guide →</a>
|
||||||
|
<a href="reference/extensions.md" class="pillar-link">Extensions reference →</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="community-section">
|
||||||
|
|
||||||
|
## Built by the community
|
||||||
|
|
||||||
|
**200+ contributors** power the Spec Kit ecosystem — from core integrations to entirely new development processes. Anyone can create and publish an extension, preset, or workflow.
|
||||||
|
|
||||||
|
<div class="stats-grid">
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">96K+</span>
|
||||||
|
<span class="stat-label">GitHub stars</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">200+</span>
|
||||||
|
<span class="stat-label">Contributors</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">30</span>
|
||||||
|
<span class="stat-label">Integrations</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">91</span>
|
||||||
|
<span class="stat-label">Extensions</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">18</span>
|
||||||
|
<span class="stat-label">Presets</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-number">4</span>
|
||||||
|
<span class="stat-label">Friends projects</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="community/presets.md">Presets</a> · <a href="community/walkthroughs.md">Walkthroughs</a> · <a href="community/friends.md">Friends</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explore the docs
|
||||||
|
|
||||||
|
<div class="nav-cards">
|
||||||
|
<a href="quickstart.md" class="nav-card">
|
||||||
|
<strong>Getting Started</strong>
|
||||||
|
<span>Install, configure, and run your first SDD workflow</span>
|
||||||
|
</a>
|
||||||
|
<a href="reference/overview.md" class="nav-card">
|
||||||
|
<strong>Reference</strong>
|
||||||
|
<span>Core commands, integrations, extensions, presets, and workflows</span>
|
||||||
|
</a>
|
||||||
|
<a href="community/presets.md" class="nav-card">
|
||||||
|
<strong>Community</strong>
|
||||||
|
<span>Presets, walkthroughs, and friend projects</span>
|
||||||
|
</a>
|
||||||
|
<a href="local-development.md" class="nav-card">
|
||||||
|
<strong>Development</strong>
|
||||||
|
<span>Contribute to Spec Kit</span>
|
||||||
|
</a>
|
||||||
|
<a href="concepts/sdd.md" class="nav-card">
|
||||||
|
<strong>What is SDD?</strong>
|
||||||
|
<span>The philosophy behind Spec-Driven Development</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="footer-cta">
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvx --from git+https://github.com/github/spec-kit.git
|
||||||
|
specify init my-project --integration copilot
|
||||||
|
```
|
||||||
|
|
||||||
|
Ready to start? Follow the [Quick Start Guide](quickstart.md).
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
|
|||||||
| [Junie](https://junie.jetbrains.com/) | `junie` | |
|
| [Junie](https://junie.jetbrains.com/) | `junie` | |
|
||||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | `kilocode` | |
|
| [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 |
|
| [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` |
|
| [Kiro CLI](https://kiro.dev/docs/cli/) | `kiro-cli` | Kiro CLI does not substitute `$ARGUMENTS` in file-based prompts, so Spec Kit ships a prose fallback at render time (see [Manage prompts](https://kiro.dev/docs/cli/chat/manage-prompts/) and issue [#1926](https://github.com/github/spec-kit/issues/1926)). Alias: `--integration kiro` |
|
||||||
| [Lingma](https://lingma.aliyun.com/) | `lingma` | Skills-based integration; skills are installed automatically |
|
| [Lingma](https://lingma.aliyun.com/) | `lingma` | Skills-based integration; skills are installed automatically |
|
||||||
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | `vibe` | |
|
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | `vibe` | |
|
||||||
| [opencode](https://opencode.ai/) | `opencode` | |
|
| [opencode](https://opencode.ai/) | `opencode` | |
|
||||||
@@ -65,6 +65,8 @@ Installing an additional integration does not change the default integration. Us
|
|||||||
|
|
||||||
> **Note:** All integration management commands require a project already initialized with `specify init`. To start a new project with a specific agent, use `specify init <project> --integration <key>` instead.
|
> **Note:** All integration management commands require a project already initialized with `specify init`. To start a new project with a specific agent, use `specify init <project> --integration <key>` instead.
|
||||||
|
|
||||||
|
**Version note:** Controlled multi-install support was introduced in Spec Kit 0.8.5. If `specify integration install <key>` says another integration is already installed and only suggests `switch` or `uninstall`, check your local CLI with `specify version` and upgrade it. Running a one-shot command such as `uvx --from git+https://github.com/github/spec-kit.git specify ...` uses a temporary copy for that command only; it does not update the persistent `specify` executable on your `PATH`.
|
||||||
|
|
||||||
## Uninstall an Integration
|
## Uninstall an Integration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
264
docs/template/public/main.css
vendored
Normal file
264
docs/template/public/main.css
vendored
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
/* Spec Kit landing page — GitHub Primer colors */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* GitHub Primer palette */
|
||||||
|
--gh-blue: #0969da;
|
||||||
|
--gh-green: #1a7f37;
|
||||||
|
--gh-purple: #8250df;
|
||||||
|
--gh-coral: #cf222e;
|
||||||
|
--gh-orange: #bf8700;
|
||||||
|
--gh-blue-subtle: #ddf4ff;
|
||||||
|
--gh-green-subtle: #dafbe1;
|
||||||
|
--gh-purple-subtle: #fbefff;
|
||||||
|
--gh-coral-subtle: #ffebe9;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] {
|
||||||
|
--gh-blue: #58a6ff;
|
||||||
|
--gh-green: #3fb950;
|
||||||
|
--gh-purple: #bc8cff;
|
||||||
|
--gh-coral: #f85149;
|
||||||
|
--gh-orange: #d29922;
|
||||||
|
--gh-blue-subtle: #0d1d30;
|
||||||
|
--gh-green-subtle: #0d1d14;
|
||||||
|
--gh-purple-subtle: #1c0d2e;
|
||||||
|
--gh-coral-subtle: #2d0f0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override Bootstrap primary with GitHub blue */
|
||||||
|
body[data-layout="landing"] {
|
||||||
|
--bs-primary: var(--gh-blue);
|
||||||
|
--bs-primary-rgb: 9, 105, 218;
|
||||||
|
--bs-link-color: var(--gh-blue);
|
||||||
|
--bs-link-hover-color: var(--gh-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] body[data-layout="landing"],
|
||||||
|
body[data-layout="landing"][data-bs-theme="dark"] {
|
||||||
|
--bs-primary-rgb: 88, 166, 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero section */
|
||||||
|
.landing-hero {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem 0 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero h1 {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
font-weight: 800;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
background: linear-gradient(135deg, var(--gh-blue), var(--gh-purple));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero p {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
max-width: 640px;
|
||||||
|
margin: 0 auto 1.5rem;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero .btn-primary {
|
||||||
|
background-color: var(--gh-blue);
|
||||||
|
border-color: var(--gh-blue);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero .btn-primary:hover {
|
||||||
|
background-color: #0860ca;
|
||||||
|
border-color: #0860ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero .btn-outline-primary {
|
||||||
|
color: var(--gh-blue);
|
||||||
|
border-color: var(--gh-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-hero .btn-outline-primary:hover {
|
||||||
|
background-color: var(--gh-blue);
|
||||||
|
border-color: var(--gh-blue);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pillar cards grid */
|
||||||
|
.pillar-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pillar-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card {
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background: var(--bs-body-bg);
|
||||||
|
transition: box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||||
|
border-top: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each pillar gets a distinct GitHub color accent */
|
||||||
|
.pillar-card:nth-child(1) { border-top-color: var(--gh-green); }
|
||||||
|
.pillar-card:nth-child(2) { border-top-color: var(--gh-blue); }
|
||||||
|
.pillar-card:nth-child(3) { border-top-color: var(--gh-purple); }
|
||||||
|
.pillar-card:nth-child(4) { border-top-color: var(--gh-coral); }
|
||||||
|
|
||||||
|
.pillar-card:nth-child(1):hover { box-shadow: 0 4px 16px rgba(26, 127, 55, 0.12); }
|
||||||
|
.pillar-card:nth-child(2):hover { box-shadow: 0 4px 16px rgba(9, 105, 218, 0.12); }
|
||||||
|
.pillar-card:nth-child(3):hover { box-shadow: 0 4px 16px rgba(130, 80, 223, 0.12); }
|
||||||
|
.pillar-card:nth-child(4):hover { box-shadow: 0 4px 16px rgba(207, 34, 46, 0.12); }
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .pillar-card:nth-child(1):hover { box-shadow: 0 4px 16px rgba(63, 185, 80, 0.15); }
|
||||||
|
[data-bs-theme="dark"] .pillar-card:nth-child(2):hover { box-shadow: 0 4px 16px rgba(88, 166, 255, 0.15); }
|
||||||
|
[data-bs-theme="dark"] .pillar-card:nth-child(3):hover { box-shadow: 0 4px 16px rgba(188, 140, 255, 0.15); }
|
||||||
|
[data-bs-theme="dark"] .pillar-card:nth-child(4):hover { box-shadow: 0 4px 16px rgba(248, 81, 73, 0.15); }
|
||||||
|
|
||||||
|
.pillar-card h3 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pillar headings pick up their card's accent color */
|
||||||
|
.pillar-card:nth-child(1) h3 { color: var(--gh-green); }
|
||||||
|
.pillar-card:nth-child(2) h3 { color: var(--gh-blue); }
|
||||||
|
.pillar-card:nth-child(3) h3 { color: var(--gh-purple); }
|
||||||
|
.pillar-card:nth-child(4) h3 { color: var(--gh-coral); }
|
||||||
|
|
||||||
|
.pillar-card .pillar-stat {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gh-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card:nth-child(3) .pillar-stat {
|
||||||
|
color: var(--gh-purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card ul {
|
||||||
|
padding-left: 1.2rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card .pillar-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pillar-card:nth-child(1) .pillar-link { color: var(--gh-blue); }
|
||||||
|
.pillar-card:nth-child(2) .pillar-link { color: var(--gh-green); }
|
||||||
|
.pillar-card:nth-child(3) .pillar-link { color: var(--gh-purple); }
|
||||||
|
.pillar-card:nth-child(4) .pillar-link { color: var(--gh-coral); }
|
||||||
|
|
||||||
|
/* Community stats section */
|
||||||
|
.community-section {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1.5rem auto;
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.stats-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item .stat-number {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gh-blue);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item .stat-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nav cards */
|
||||||
|
.nav-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.nav-cards {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-card {
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
transition: box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||||
|
display: block;
|
||||||
|
border-left: 3px solid var(--gh-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-card:hover {
|
||||||
|
border-color: var(--gh-blue);
|
||||||
|
border-left-color: var(--gh-blue);
|
||||||
|
box-shadow: 0 2px 8px rgba(9, 105, 218, 0.1);
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .nav-card:hover {
|
||||||
|
box-shadow: 0 2px 8px rgba(88, 166, 255, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-card strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
color: var(--gh-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-card span {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer CTA */
|
||||||
|
.footer-cta {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-cta code {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
@@ -30,6 +30,12 @@
|
|||||||
- name: Workflows
|
- name: Workflows
|
||||||
href: reference/workflows.md
|
href: reference/workflows.md
|
||||||
|
|
||||||
|
# Concepts
|
||||||
|
- name: Concepts
|
||||||
|
items:
|
||||||
|
- name: What is SDD?
|
||||||
|
href: concepts/sdd.md
|
||||||
|
|
||||||
# Development workflows
|
# Development workflows
|
||||||
- name: Development
|
- name: Development
|
||||||
items:
|
items:
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
|
|
||||||
The CLI tool (`specify`) is separate from your project files. Upgrade it to get the latest features and bug fixes.
|
The CLI tool (`specify`) is separate from your project files. Upgrade it to get the latest features and bug fixes.
|
||||||
|
|
||||||
|
Before upgrading, you can check whether a newer released version is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify self check
|
||||||
|
```
|
||||||
|
|
||||||
### If you installed with `uv tool install`
|
### If you installed with `uv tool install`
|
||||||
|
|
||||||
Upgrade to a specific release (check [Releases](https://github.com/github/spec-kit/releases) for the latest tag):
|
Upgrade to a specific release (check [Releases](https://github.com/github/spec-kit/releases) for the latest tag):
|
||||||
@@ -35,6 +41,8 @@ Specify the desired release tag:
|
|||||||
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here --integration copilot
|
uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here --integration copilot
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`uvx` runs a temporary copy of Spec Kit for that single command. It does not update a persistent `specify` installed with `uv tool install`, `pipx`, or another tool manager. If a newer feature works through `uvx` but your local `specify` still reports an older version, upgrade the persistent CLI with the command that matches your install method.
|
||||||
|
|
||||||
### If you installed with `pipx`
|
### If you installed with `pipx`
|
||||||
|
|
||||||
Upgrade to a specific release:
|
Upgrade to a specific release:
|
||||||
@@ -49,7 +57,7 @@ pipx install --force git+https://github.com/github/spec-kit.git@vX.Y.Z
|
|||||||
specify check
|
specify check
|
||||||
```
|
```
|
||||||
|
|
||||||
This shows installed tools and confirms the CLI is working.
|
This shows installed tools and confirms the CLI is working. Use `specify version` to confirm which persistent CLI version is currently on your `PATH`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"updated_at": "2026-05-07T20:05:00Z",
|
"updated_at": "2026-05-12T21:40:51Z",
|
||||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"aide": {
|
"aide": {
|
||||||
@@ -174,8 +174,8 @@
|
|||||||
"id": "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.",
|
"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",
|
"author": "DyanGalih",
|
||||||
"version": "1.8.0",
|
"version": "1.8.4",
|
||||||
"download_url": "https://github.com/DyanGalih/spec-kit-architecture-guard/archive/refs/tags/v1.8.0.zip",
|
"download_url": "https://github.com/DyanGalih/spec-kit-architecture-guard/archive/refs/tags/v1.8.4.zip",
|
||||||
"repository": "https://github.com/DyanGalih/spec-kit-architecture-guard",
|
"repository": "https://github.com/DyanGalih/spec-kit-architecture-guard",
|
||||||
"homepage": "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",
|
"documentation": "https://github.com/DyanGalih/spec-kit-architecture-guard/blob/main/README.md",
|
||||||
@@ -185,8 +185,8 @@
|
|||||||
"speckit_version": ">=0.1.0"
|
"speckit_version": ">=0.1.0"
|
||||||
},
|
},
|
||||||
"provides": {
|
"provides": {
|
||||||
"commands": 6,
|
"commands": 10,
|
||||||
"hooks": 0
|
"hooks": 3
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"architecture",
|
"architecture",
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"downloads": 0,
|
"downloads": 0,
|
||||||
"stars": 0,
|
"stars": 0,
|
||||||
"created_at": "2026-05-05T07:26:00Z",
|
"created_at": "2026-05-05T07:26:00Z",
|
||||||
"updated_at": "2026-05-07T15:37:14Z"
|
"updated_at": "2026-05-11T14:58:00Z"
|
||||||
},
|
},
|
||||||
"archive": {
|
"archive": {
|
||||||
"name": "Archive Extension",
|
"name": "Archive Extension",
|
||||||
@@ -368,6 +368,38 @@
|
|||||||
"created_at": "2026-04-10T00:00:00Z",
|
"created_at": "2026-04-10T00:00:00Z",
|
||||||
"updated_at": "2026-04-10T00:00:00Z"
|
"updated_at": "2026-04-10T00:00:00Z"
|
||||||
},
|
},
|
||||||
|
"brownkit": {
|
||||||
|
"name": "BrownKit \u2014 Brownfield Discovery for Spec-Kit",
|
||||||
|
"id": "brownkit",
|
||||||
|
"description": "Evidence-driven capability discovery, security and QA risk assessment for existing codebases.",
|
||||||
|
"author": "Maksim Shautsou",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"download_url": "https://github.com/MaksimShevtsov/BrownKit/archive/refs/tags/v1.0.1.zip",
|
||||||
|
"repository": "https://github.com/MaksimShevtsov/BrownKit",
|
||||||
|
"homepage": "https://github.com/MaksimShevtsov/BrownKit",
|
||||||
|
"documentation": "https://github.com/MaksimShevtsov/BrownKit/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/MaksimShevtsov/BrownKit/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 10,
|
||||||
|
"hooks": 5
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"brownfield",
|
||||||
|
"discovery",
|
||||||
|
"security",
|
||||||
|
"qa",
|
||||||
|
"capabilities"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-05-10T00:00:00Z",
|
||||||
|
"updated_at": "2026-05-10T00:00:00Z"
|
||||||
|
},
|
||||||
"bugfix": {
|
"bugfix": {
|
||||||
"name": "Bugfix Workflow",
|
"name": "Bugfix Workflow",
|
||||||
"id": "bugfix",
|
"id": "bugfix",
|
||||||
@@ -467,6 +499,38 @@
|
|||||||
"created_at": "2026-04-16T00:00:00Z",
|
"created_at": "2026-04-16T00:00:00Z",
|
||||||
"updated_at": "2026-04-16T00:00:00Z"
|
"updated_at": "2026-04-16T00:00:00Z"
|
||||||
},
|
},
|
||||||
|
"changelog": {
|
||||||
|
"name": "Spec Changelog",
|
||||||
|
"id": "changelog",
|
||||||
|
"description": "Auto-generate changelogs and release notes from spec git history and requirement diffs.",
|
||||||
|
"author": "Quratulain-bilal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/Quratulain-bilal/spec-kit-changelog/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/Quratulain-bilal/spec-kit-changelog",
|
||||||
|
"homepage": "https://github.com/Quratulain-bilal/spec-kit-changelog",
|
||||||
|
"documentation": "https://github.com/Quratulain-bilal/spec-kit-changelog/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/Quratulain-bilal/spec-kit-changelog/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.4.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 4,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"changelog",
|
||||||
|
"release-notes",
|
||||||
|
"documentation",
|
||||||
|
"git-history",
|
||||||
|
"notifications"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-04-11T18:00:00Z",
|
||||||
|
"updated_at": "2026-04-11T18:00:00Z"
|
||||||
|
},
|
||||||
"ci-guard": {
|
"ci-guard": {
|
||||||
"name": "CI Guard",
|
"name": "CI Guard",
|
||||||
"id": "ci-guard",
|
"id": "ci-guard",
|
||||||
@@ -1481,8 +1545,8 @@
|
|||||||
"id": "memory-md",
|
"id": "memory-md",
|
||||||
"description": "Spec Kit extension for repository-native Markdown memory that captures durable decisions, bugs, and project context",
|
"description": "Spec Kit extension for repository-native Markdown memory that captures durable decisions, bugs, and project context",
|
||||||
"author": "DyanGalih",
|
"author": "DyanGalih",
|
||||||
"version": "0.8.0",
|
"version": "0.8.5",
|
||||||
"download_url": "https://github.com/DyanGalih/spec-kit-memory-hub/archive/refs/tags/v0.8.0.zip",
|
"download_url": "https://github.com/DyanGalih/spec-kit-memory-hub/archive/refs/tags/v0.8.5.zip",
|
||||||
"repository": "https://github.com/DyanGalih/spec-kit-memory-hub",
|
"repository": "https://github.com/DyanGalih/spec-kit-memory-hub",
|
||||||
"homepage": "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",
|
"documentation": "https://github.com/DyanGalih/spec-kit-memory-hub/blob/main/README.md",
|
||||||
@@ -1492,8 +1556,8 @@
|
|||||||
"speckit_version": ">=0.2.0"
|
"speckit_version": ">=0.2.0"
|
||||||
},
|
},
|
||||||
"provides": {
|
"provides": {
|
||||||
"commands": 6,
|
"commands": 7,
|
||||||
"hooks": 0
|
"hooks": 2
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"memory",
|
"memory",
|
||||||
@@ -1507,7 +1571,7 @@
|
|||||||
"downloads": 0,
|
"downloads": 0,
|
||||||
"stars": 0,
|
"stars": 0,
|
||||||
"created_at": "2026-04-23T00:00:00Z",
|
"created_at": "2026-04-23T00:00:00Z",
|
||||||
"updated_at": "2026-05-07T15:37:14Z"
|
"updated_at": "2026-05-11T14:58:00Z"
|
||||||
},
|
},
|
||||||
"memorylint": {
|
"memorylint": {
|
||||||
"name": "MemoryLint",
|
"name": "MemoryLint",
|
||||||
@@ -2215,8 +2279,8 @@
|
|||||||
"id": "security-review",
|
"id": "security-review",
|
||||||
"description": "Full-project secure-by-design security audits plus staged, branch/PR, plan, task, follow-up, and apply reviews",
|
"description": "Full-project secure-by-design security audits plus staged, branch/PR, plan, task, follow-up, and apply reviews",
|
||||||
"author": "DyanGalih",
|
"author": "DyanGalih",
|
||||||
"version": "1.4.5",
|
"version": "1.5.0",
|
||||||
"download_url": "https://github.com/DyanGalih/spec-kit-security-review/archive/refs/tags/v1.4.5.zip",
|
"download_url": "https://github.com/DyanGalih/spec-kit-security-review/archive/refs/tags/v1.5.0.zip",
|
||||||
"repository": "https://github.com/DyanGalih/spec-kit-security-review",
|
"repository": "https://github.com/DyanGalih/spec-kit-security-review",
|
||||||
"homepage": "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",
|
"documentation": "https://github.com/DyanGalih/spec-kit-security-review/blob/main/README.md",
|
||||||
@@ -2226,8 +2290,8 @@
|
|||||||
"speckit_version": ">=0.1.0"
|
"speckit_version": ">=0.1.0"
|
||||||
},
|
},
|
||||||
"provides": {
|
"provides": {
|
||||||
"commands": 7,
|
"commands": 9,
|
||||||
"hooks": 0
|
"hooks": 3
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"security",
|
"security",
|
||||||
@@ -2240,7 +2304,7 @@
|
|||||||
"downloads": 0,
|
"downloads": 0,
|
||||||
"stars": 0,
|
"stars": 0,
|
||||||
"created_at": "2026-04-03T03:24:03Z",
|
"created_at": "2026-04-03T03:24:03Z",
|
||||||
"updated_at": "2026-05-06T22:28:55Z"
|
"updated_at": "2026-05-11T14:58:00Z"
|
||||||
},
|
},
|
||||||
"sf": {
|
"sf": {
|
||||||
"name": "SFSpeckit — Salesforce Spec-Driven Development",
|
"name": "SFSpeckit — Salesforce Spec-Driven Development",
|
||||||
|
|||||||
@@ -256,6 +256,43 @@
|
|||||||
"created_at": "2026-04-09T08:00:00Z",
|
"created_at": "2026-04-09T08:00:00Z",
|
||||||
"updated_at": "2026-04-27T08:00:00Z"
|
"updated_at": "2026-04-27T08:00:00Z"
|
||||||
},
|
},
|
||||||
|
"game-narrative-writing": {
|
||||||
|
"name": "Game Narrative Writing",
|
||||||
|
"id": "game-narrative-writing",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Spec-Driven Development for interactive game-narrative pre-production in video games. Authors write in a portable generic format, Twine/Sugarcube (.twee) or Ink (.ink). Covers choice-IF, visual novels, and branching dialogue. Supports Tier 1 mechanic hooks (flag, counter, inventory, timer, trust, currency, npc_state, ending_condition), multi-ending design, series carry-over variable registry, and NPC-focused character architecture.",
|
||||||
|
"author": "Andreas Daumann",
|
||||||
|
"repository": "https://github.com/adaumann/speckit-preset-game-narrative-writing",
|
||||||
|
"download_url": "https://github.com/adaumann/speckit-preset-game-narrative-writing/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"homepage": "https://github.com/adaumann/speckit-preset-game-narrative-writing",
|
||||||
|
"documentation": "https://github.com/adaumann/speckit-preset-game-narrative-writing/blob/main/game-narrative-writing/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.5.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"templates": 22,
|
||||||
|
"commands": 36,
|
||||||
|
"scripts": 2
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"game-writing",
|
||||||
|
"interactive-fiction",
|
||||||
|
"twine",
|
||||||
|
"ink",
|
||||||
|
"renpy",
|
||||||
|
"point-and-click",
|
||||||
|
"branching-narrative",
|
||||||
|
"choice-if",
|
||||||
|
"visual-novel",
|
||||||
|
"mechanic-hooks",
|
||||||
|
"game-narrative",
|
||||||
|
"export",
|
||||||
|
"series"
|
||||||
|
],
|
||||||
|
"created_at": "2026-05-05T08:00:00Z",
|
||||||
|
"updated_at": "2026-05-05T08:00:00Z"
|
||||||
|
},
|
||||||
"isaqb-architecture-governance": {
|
"isaqb-architecture-governance": {
|
||||||
"name": "iSAQB Architecture Governance",
|
"name": "iSAQB Architecture Governance",
|
||||||
"id": "isaqb-architecture-governance",
|
"id": "isaqb-architecture-governance",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "specify-cli"
|
name = "specify-cli"
|
||||||
version = "0.8.8.dev0"
|
version = "0.8.9"
|
||||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
180
src/specify_cli/catalogs.py
Normal file
180
src/specify_cli/catalogs.py
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
"""Shared catalog stack config primitives.
|
||||||
|
|
||||||
|
Catalog-backed features use the same local config shape and URL validation
|
||||||
|
rules. This module keeps those narrow primitives in one place while individual
|
||||||
|
catalog types keep their active source resolution, fetch, cache, and
|
||||||
|
domain-specific validation behavior.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CatalogEntry:
|
||||||
|
"""Represents a single catalog source in a catalog stack."""
|
||||||
|
|
||||||
|
url: str
|
||||||
|
name: str
|
||||||
|
priority: int
|
||||||
|
install_allowed: bool
|
||||||
|
description: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogStackBase:
|
||||||
|
"""Base class for ordered catalog-source resolution.
|
||||||
|
|
||||||
|
Subclasses provide catalog-specific metadata and exception classes. Fetching
|
||||||
|
and schema validation stay in each concrete catalog because those formats
|
||||||
|
differ across integrations, extensions, presets, and workflows.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ENTRY_CLASS: ClassVar[type[CatalogEntry]] = CatalogEntry
|
||||||
|
ERROR_TYPE: ClassVar[type[Exception]] = ValueError
|
||||||
|
VALIDATION_ERROR_TYPE: ClassVar[type[Exception]] = ValueError
|
||||||
|
|
||||||
|
CONFIG_FILENAME: ClassVar[str]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _error(cls, message: str) -> Exception:
|
||||||
|
return cls.ERROR_TYPE(message)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _validation_error(cls, message: str) -> Exception:
|
||||||
|
return cls.VALIDATION_ERROR_TYPE(message)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _entry(
|
||||||
|
cls,
|
||||||
|
*,
|
||||||
|
url: str,
|
||||||
|
name: str,
|
||||||
|
priority: int,
|
||||||
|
install_allowed: bool,
|
||||||
|
description: str = "",
|
||||||
|
) -> CatalogEntry:
|
||||||
|
return cls.ENTRY_CLASS(
|
||||||
|
url=url,
|
||||||
|
name=name,
|
||||||
|
priority=priority,
|
||||||
|
install_allowed=install_allowed,
|
||||||
|
description=description,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _validate_catalog_url(cls, url: str) -> None:
|
||||||
|
"""Validate that a catalog URL uses HTTPS, except localhost HTTP."""
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
parsed = urlparse(url)
|
||||||
|
is_localhost = parsed.hostname in ("localhost", "127.0.0.1", "::1")
|
||||||
|
if parsed.scheme != "https" and not (parsed.scheme == "http" and is_localhost):
|
||||||
|
raise cls._error(
|
||||||
|
f"Catalog URL must use HTTPS (got {parsed.scheme}://). "
|
||||||
|
"HTTP is only allowed for localhost."
|
||||||
|
)
|
||||||
|
if not parsed.netloc:
|
||||||
|
raise cls._error("Catalog URL must be a valid URL with a host.")
|
||||||
|
|
||||||
|
def _load_catalog_config(self, config_path: Path) -> list[CatalogEntry] | None:
|
||||||
|
"""Load catalog stack configuration from a YAML file.
|
||||||
|
|
||||||
|
Returns ``None`` when the file does not exist. Existing files fail
|
||||||
|
closed when they are malformed, empty, or contain no usable URLs.
|
||||||
|
"""
|
||||||
|
if not config_path.exists():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
data = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
||||||
|
except (yaml.YAMLError, OSError, UnicodeError) as exc:
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Failed to read catalog config {config_path}: {exc}"
|
||||||
|
) from exc
|
||||||
|
if data is None:
|
||||||
|
data = {}
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog config {config_path}: expected a YAML mapping at the root"
|
||||||
|
)
|
||||||
|
|
||||||
|
catalogs_data = data.get("catalogs", [])
|
||||||
|
if not isinstance(catalogs_data, list):
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog config {config_path}: 'catalogs' must be a list, "
|
||||||
|
f"got {type(catalogs_data).__name__}"
|
||||||
|
)
|
||||||
|
if not catalogs_data:
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Catalog config {config_path} exists but contains no 'catalogs' entries. "
|
||||||
|
f"Remove the file to use built-in defaults, or add valid catalog entries."
|
||||||
|
)
|
||||||
|
|
||||||
|
entries: list[CatalogEntry] = []
|
||||||
|
skipped: list[int] = []
|
||||||
|
for idx, item in enumerate(catalogs_data):
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog config {config_path}: catalog entry at index {idx}: "
|
||||||
|
f"expected a mapping, got {type(item).__name__}"
|
||||||
|
)
|
||||||
|
url = str(item.get("url", "")).strip()
|
||||||
|
if not url:
|
||||||
|
skipped.append(idx)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self._validate_catalog_url(url)
|
||||||
|
except self.ERROR_TYPE as exc:
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog URL in {config_path} at index {idx}: {exc}"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
raw_priority = item.get("priority", idx + 1)
|
||||||
|
if isinstance(raw_priority, bool):
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog config {config_path}: "
|
||||||
|
f"Invalid priority for catalog '{item.get('name', idx + 1)}': "
|
||||||
|
f"expected integer, got {raw_priority!r}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
priority = int(raw_priority)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Invalid catalog config {config_path}: "
|
||||||
|
f"Invalid priority for catalog '{item.get('name', idx + 1)}': "
|
||||||
|
f"expected integer, got {raw_priority!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_install = item.get("install_allowed", False)
|
||||||
|
if isinstance(raw_install, str):
|
||||||
|
install_allowed = raw_install.strip().lower() in ("true", "yes", "1")
|
||||||
|
else:
|
||||||
|
install_allowed = bool(raw_install)
|
||||||
|
|
||||||
|
raw_name = item.get("name")
|
||||||
|
name = str(raw_name).strip() if raw_name is not None else ""
|
||||||
|
if not name:
|
||||||
|
name = f"catalog-{len(entries) + 1}"
|
||||||
|
|
||||||
|
entries.append(
|
||||||
|
self._entry(
|
||||||
|
url=url,
|
||||||
|
name=name,
|
||||||
|
priority=priority,
|
||||||
|
install_allowed=install_allowed,
|
||||||
|
description=str(item.get("description", "")),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
entries.sort(key=lambda e: e.priority)
|
||||||
|
if not entries:
|
||||||
|
raise self._validation_error(
|
||||||
|
f"Catalog config {config_path} contains {len(catalogs_data)} "
|
||||||
|
f"entries but none have valid URLs (entries at indices {skipped} "
|
||||||
|
f"were skipped). Each catalog entry must have a 'url' field."
|
||||||
|
)
|
||||||
|
return entries
|
||||||
@@ -21,6 +21,8 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
import yaml
|
import yaml
|
||||||
from packaging import version as pkg_version
|
from packaging import version as pkg_version
|
||||||
|
|
||||||
|
from ..catalogs import CatalogEntry, CatalogStackBase
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Errors
|
# Errors
|
||||||
@@ -43,21 +45,15 @@ class IntegrationDescriptorError(Exception):
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IntegrationCatalogEntry:
|
class IntegrationCatalogEntry(CatalogEntry):
|
||||||
"""Represents a single catalog source in the catalog stack."""
|
"""Represents a single catalog source in the catalog stack."""
|
||||||
|
|
||||||
url: str
|
|
||||||
name: str
|
|
||||||
priority: int
|
|
||||||
install_allowed: bool
|
|
||||||
description: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# IntegrationCatalog
|
# IntegrationCatalog
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class IntegrationCatalog:
|
class IntegrationCatalog(CatalogStackBase):
|
||||||
"""Manages integration catalog fetching, caching, and searching."""
|
"""Manages integration catalog fetching, caching, and searching."""
|
||||||
|
|
||||||
DEFAULT_CATALOG_URL = (
|
DEFAULT_CATALOG_URL = (
|
||||||
@@ -67,136 +63,15 @@ class IntegrationCatalog:
|
|||||||
"https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.community.json"
|
"https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.community.json"
|
||||||
)
|
)
|
||||||
CACHE_DURATION = 3600 # 1 hour
|
CACHE_DURATION = 3600 # 1 hour
|
||||||
|
CONFIG_FILENAME = "integration-catalogs.yml"
|
||||||
|
ENTRY_CLASS = IntegrationCatalogEntry
|
||||||
|
ERROR_TYPE = IntegrationCatalogError
|
||||||
|
VALIDATION_ERROR_TYPE = IntegrationValidationError
|
||||||
|
|
||||||
def __init__(self, project_root: Path) -> None:
|
def __init__(self, project_root: Path) -> None:
|
||||||
self.project_root = project_root
|
self.project_root = project_root
|
||||||
self.cache_dir = project_root / ".specify" / "integrations" / ".cache"
|
self.cache_dir = project_root / ".specify" / "integrations" / ".cache"
|
||||||
|
|
||||||
# -- URL validation ---------------------------------------------------
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _validate_catalog_url(url: str) -> None:
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
parsed = urlparse(url)
|
|
||||||
is_localhost = parsed.hostname in ("localhost", "127.0.0.1", "::1")
|
|
||||||
if parsed.scheme != "https" and not (parsed.scheme == "http" and is_localhost):
|
|
||||||
raise IntegrationCatalogError(
|
|
||||||
f"Catalog URL must use HTTPS (got {parsed.scheme}://). "
|
|
||||||
"HTTP is only allowed for localhost."
|
|
||||||
)
|
|
||||||
if not parsed.netloc:
|
|
||||||
raise IntegrationCatalogError(
|
|
||||||
"Catalog URL must be a valid URL with a host."
|
|
||||||
)
|
|
||||||
|
|
||||||
# -- Catalog stack ----------------------------------------------------
|
|
||||||
|
|
||||||
def _load_catalog_config(
|
|
||||||
self, config_path: Path
|
|
||||||
) -> Optional[List[IntegrationCatalogEntry]]:
|
|
||||||
"""Load catalog stack from a YAML file.
|
|
||||||
|
|
||||||
Returns None when the file does not exist.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
IntegrationValidationError: on any local-config / YAML problem
|
|
||||||
(parse failures, wrong shape, missing/invalid fields,
|
|
||||||
invalid catalog URLs, etc.). This is a subclass of
|
|
||||||
:class:`IntegrationCatalogError`, so any caller that already
|
|
||||||
catches ``IntegrationCatalogError`` keeps working — but
|
|
||||||
callers that want to distinguish *local config* problems
|
|
||||||
from *remote/network* problems can match the subclass.
|
|
||||||
"""
|
|
||||||
if not config_path.exists():
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
data = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
|
||||||
except (yaml.YAMLError, OSError, UnicodeError) as exc:
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Failed to read catalog config {config_path}: {exc}"
|
|
||||||
) from exc
|
|
||||||
if data is None:
|
|
||||||
data = {}
|
|
||||||
if not isinstance(data, dict):
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog config {config_path}: expected a YAML mapping at the root"
|
|
||||||
)
|
|
||||||
catalogs_data = data.get("catalogs", [])
|
|
||||||
if not isinstance(catalogs_data, list):
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog config {config_path}: 'catalogs' must be a list, "
|
|
||||||
f"got {type(catalogs_data).__name__}"
|
|
||||||
)
|
|
||||||
if not catalogs_data:
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Catalog config {config_path} exists but contains no 'catalogs' entries. "
|
|
||||||
f"Remove the file to use built-in defaults, or add valid catalog entries."
|
|
||||||
)
|
|
||||||
entries: List[IntegrationCatalogEntry] = []
|
|
||||||
skipped: List[int] = []
|
|
||||||
for idx, item in enumerate(catalogs_data):
|
|
||||||
if not isinstance(item, dict):
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog config {config_path}: catalog entry at index {idx}: "
|
|
||||||
f"expected a mapping, got {type(item).__name__}"
|
|
||||||
)
|
|
||||||
url = str(item.get("url", "")).strip()
|
|
||||||
if not url:
|
|
||||||
skipped.append(idx)
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
self._validate_catalog_url(url)
|
|
||||||
except IntegrationCatalogError as exc:
|
|
||||||
# ``_validate_catalog_url`` raises the base class for direct
|
|
||||||
# callers (e.g. ``add_catalog`` validating user input); when
|
|
||||||
# the bad URL came from a local config file, surface it as a
|
|
||||||
# validation error so CLI handlers can route it accordingly.
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog URL in {config_path} at index {idx}: {exc}"
|
|
||||||
) from exc
|
|
||||||
raw_priority = item.get("priority", idx + 1)
|
|
||||||
if isinstance(raw_priority, bool):
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog config {config_path}: "
|
|
||||||
f"Invalid priority for catalog '{item.get('name', idx + 1)}': "
|
|
||||||
f"expected integer, got {raw_priority!r}"
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
priority = int(raw_priority)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Invalid catalog config {config_path}: "
|
|
||||||
f"Invalid priority for catalog '{item.get('name', idx + 1)}': "
|
|
||||||
f"expected integer, got {raw_priority!r}"
|
|
||||||
)
|
|
||||||
raw_install = item.get("install_allowed", False)
|
|
||||||
if isinstance(raw_install, str):
|
|
||||||
install_allowed = raw_install.strip().lower() in ("true", "yes", "1")
|
|
||||||
else:
|
|
||||||
install_allowed = bool(raw_install)
|
|
||||||
raw_name = item.get("name")
|
|
||||||
name = str(raw_name).strip() if raw_name is not None else ""
|
|
||||||
if not name:
|
|
||||||
name = f"catalog-{len(entries) + 1}"
|
|
||||||
entries.append(
|
|
||||||
IntegrationCatalogEntry(
|
|
||||||
url=url,
|
|
||||||
name=name,
|
|
||||||
priority=priority,
|
|
||||||
install_allowed=install_allowed,
|
|
||||||
description=str(item.get("description", "")),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
entries.sort(key=lambda e: e.priority)
|
|
||||||
if not entries:
|
|
||||||
raise IntegrationValidationError(
|
|
||||||
f"Catalog config {config_path} contains {len(catalogs_data)} "
|
|
||||||
f"entries but none have valid URLs (entries at indices {skipped} "
|
|
||||||
f"were skipped). Each catalog entry must have a 'url' field."
|
|
||||||
)
|
|
||||||
return entries
|
|
||||||
|
|
||||||
def get_active_catalogs(self) -> List[IntegrationCatalogEntry]:
|
def get_active_catalogs(self) -> List[IntegrationCatalogEntry]:
|
||||||
"""Return the ordered list of active integration catalogs.
|
"""Return the ordered list of active integration catalogs.
|
||||||
|
|
||||||
@@ -444,8 +319,6 @@ class IntegrationCatalog:
|
|||||||
|
|
||||||
# -- Catalog-source management ----------------------------------------
|
# -- Catalog-source management ----------------------------------------
|
||||||
|
|
||||||
CONFIG_FILENAME = "integration-catalogs.yml"
|
|
||||||
|
|
||||||
def get_catalog_configs(self) -> List[Dict[str, Any]]:
|
def get_catalog_configs(self) -> List[Dict[str, Any]]:
|
||||||
"""Return the active catalog stack as a list of dicts.
|
"""Return the active catalog stack as a list of dicts.
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
from ..base import MarkdownIntegration
|
from ..base import MarkdownIntegration
|
||||||
|
|
||||||
|
|
||||||
|
# Kiro CLI file-based prompts do NOT support any argument-substitution syntax,
|
||||||
|
# so a raw "$ARGUMENTS" token would reach the model verbatim and break the
|
||||||
|
# prompt (issue #1926, kirodotdev/Kiro#4141). Use a prose fallback so the
|
||||||
|
# rendered prompt instructs the model to take its argument from the user's
|
||||||
|
# next message.
|
||||||
|
_KIRO_ARG_FALLBACK = "(the user will provide the argument in this conversation)"
|
||||||
|
|
||||||
|
|
||||||
class KiroCliIntegration(MarkdownIntegration):
|
class KiroCliIntegration(MarkdownIntegration):
|
||||||
key = "kiro-cli"
|
key = "kiro-cli"
|
||||||
config = {
|
config = {
|
||||||
@@ -15,7 +23,7 @@ class KiroCliIntegration(MarkdownIntegration):
|
|||||||
registrar_config = {
|
registrar_config = {
|
||||||
"dir": ".kiro/prompts",
|
"dir": ".kiro/prompts",
|
||||||
"format": "markdown",
|
"format": "markdown",
|
||||||
"args": "$ARGUMENTS",
|
"args": _KIRO_ARG_FALLBACK,
|
||||||
"extension": ".md",
|
"extension": ".md",
|
||||||
}
|
}
|
||||||
context_file = "AGENTS.md"
|
context_file = "AGENTS.md"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Implementation Plan: [FEATURE]
|
# Implementation Plan: [FEATURE]
|
||||||
|
|
||||||
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
||||||
|
|
||||||
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
||||||
|
|
||||||
**Note**: This template is filled in by the `__SPECKIT_COMMAND_PLAN__` command. See `.specify/templates/plan-template.md` for the execution workflow.
|
**Note**: This template is filled in by the `__SPECKIT_COMMAND_PLAN__` command. See `.specify/templates/plan-template.md` for the execution workflow.
|
||||||
@@ -17,14 +18,22 @@
|
|||||||
the iteration process.
|
the iteration process.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
**Language/Version**: [e.g., Python 3.11, Swift 5.9, Rust 1.75 or NEEDS CLARIFICATION]
|
**Language/Version**: [e.g., Python 3.11, Swift 5.9, Rust 1.75 or NEEDS CLARIFICATION]
|
||||||
**Primary Dependencies**: [e.g., FastAPI, UIKit, LLVM or NEEDS CLARIFICATION]
|
|
||||||
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
**Primary Dependencies**: [e.g., FastAPI, UIKit, LLVM or NEEDS CLARIFICATION]
|
||||||
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
|
|
||||||
|
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
||||||
|
|
||||||
|
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
|
||||||
|
|
||||||
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
|
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
|
||||||
**Project Type**: [e.g., library/cli/web-service/mobile-app/compiler/desktop-app or NEEDS CLARIFICATION]
|
|
||||||
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
|
**Project Type**: [e.g., library/cli/web-service/mobile-app/compiler/desktop-app or NEEDS CLARIFICATION]
|
||||||
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
|
|
||||||
|
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
|
||||||
|
|
||||||
|
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
|
||||||
|
|
||||||
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
||||||
|
|
||||||
## Constitution Check
|
## Constitution Check
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
# Feature Specification: [FEATURE NAME]
|
# Feature Specification: [FEATURE NAME]
|
||||||
|
|
||||||
**Feature Branch**: `[###-feature-name]`
|
**Feature Branch**: `[###-feature-name]`
|
||||||
**Created**: [DATE]
|
|
||||||
**Status**: Draft
|
**Created**: [DATE]
|
||||||
|
|
||||||
|
**Status**: Draft
|
||||||
|
|
||||||
**Input**: User description: "$ARGUMENTS"
|
**Input**: User description: "$ARGUMENTS"
|
||||||
|
|
||||||
## User Scenarios & Testing *(mandatory)*
|
## User Scenarios & Testing *(mandatory)*
|
||||||
@@ -11,7 +14,7 @@
|
|||||||
IMPORTANT: User stories should be PRIORITIZED as user journeys ordered by importance.
|
IMPORTANT: User stories should be PRIORITIZED as user journeys ordered by importance.
|
||||||
Each user story/journey must be INDEPENDENTLY TESTABLE - meaning if you implement just ONE of them,
|
Each user story/journey must be INDEPENDENTLY TESTABLE - meaning if you implement just ONE of them,
|
||||||
you should still have a viable MVP (Minimum Viable Product) that delivers value.
|
you should still have a viable MVP (Minimum Viable Product) that delivers value.
|
||||||
|
|
||||||
Assign priorities (P1, P2, P3, etc.) to each story, where P1 is the most critical.
|
Assign priorities (P1, P2, P3, etc.) to each story, where P1 is the most critical.
|
||||||
Think of each story as a standalone slice of functionality that can be:
|
Think of each story as a standalone slice of functionality that can be:
|
||||||
- Developed independently
|
- Developed independently
|
||||||
@@ -85,7 +88,7 @@
|
|||||||
### Functional Requirements
|
### Functional Requirements
|
||||||
|
|
||||||
- **FR-001**: System MUST [specific capability, e.g., "allow users to create accounts"]
|
- **FR-001**: System MUST [specific capability, e.g., "allow users to create accounts"]
|
||||||
- **FR-002**: System MUST [specific capability, e.g., "validate email addresses"]
|
- **FR-002**: System MUST [specific capability, e.g., "validate email addresses"]
|
||||||
- **FR-003**: Users MUST be able to [key interaction, e.g., "reset their password"]
|
- **FR-003**: Users MUST be able to [key interaction, e.g., "reset their password"]
|
||||||
- **FR-004**: System MUST [data requirement, e.g., "persist user preferences"]
|
- **FR-004**: System MUST [data requirement, e.g., "persist user preferences"]
|
||||||
- **FR-005**: System MUST [behavior, e.g., "log all security events"]
|
- **FR-005**: System MUST [behavior, e.g., "log all security events"]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ description: "Task list template for feature implementation"
|
|||||||
# Tasks: [FEATURE NAME]
|
# Tasks: [FEATURE NAME]
|
||||||
|
|
||||||
**Input**: Design documents from `/specs/[###-feature-name]/`
|
**Input**: Design documents from `/specs/[###-feature-name]/`
|
||||||
|
|
||||||
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
|
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
|
||||||
|
|
||||||
**Tests**: The examples below include test tasks. Tests are OPTIONAL - only include them if explicitly requested in the feature specification.
|
**Tests**: The examples below include test tasks. Tests are OPTIONAL - only include them if explicitly requested in the feature specification.
|
||||||
@@ -25,21 +26,21 @@ description: "Task list template for feature implementation"
|
|||||||
- **Mobile**: `api/src/`, `ios/src/` or `android/src/`
|
- **Mobile**: `api/src/`, `ios/src/` or `android/src/`
|
||||||
- Paths shown below assume single project - adjust based on plan.md structure
|
- Paths shown below assume single project - adjust based on plan.md structure
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
============================================================================
|
============================================================================
|
||||||
IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only.
|
IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only.
|
||||||
|
|
||||||
The __SPECKIT_COMMAND_TASKS__ command MUST replace these with actual tasks based on:
|
The __SPECKIT_COMMAND_TASKS__ command MUST replace these with actual tasks based on:
|
||||||
- User stories from spec.md (with their priorities P1, P2, P3...)
|
- User stories from spec.md (with their priorities P1, P2, P3...)
|
||||||
- Feature requirements from plan.md
|
- Feature requirements from plan.md
|
||||||
- Entities from data-model.md
|
- Entities from data-model.md
|
||||||
- Endpoints from contracts/
|
- Endpoints from contracts/
|
||||||
|
|
||||||
Tasks MUST be organized by user story so each story can be:
|
Tasks MUST be organized by user story so each story can be:
|
||||||
- Implemented independently
|
- Implemented independently
|
||||||
- Tested independently
|
- Tested independently
|
||||||
- Delivered as an MVP increment
|
- Delivered as an MVP increment
|
||||||
|
|
||||||
DO NOT keep these sample tasks in the generated tasks.md file.
|
DO NOT keep these sample tasks in the generated tasks.md file.
|
||||||
============================================================================
|
============================================================================
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1,10 +1,41 @@
|
|||||||
"""Tests for KiroCliIntegration."""
|
"""Tests for KiroCliIntegration."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from specify_cli.integrations import get_integration
|
||||||
|
from specify_cli.integrations.kiro_cli import _KIRO_ARG_FALLBACK
|
||||||
|
from specify_cli.integrations.manifest import IntegrationManifest
|
||||||
|
|
||||||
from .test_integration_base_markdown import MarkdownIntegrationTests
|
from .test_integration_base_markdown import MarkdownIntegrationTests
|
||||||
|
|
||||||
|
|
||||||
|
# Regex shapes that indicate a value is a placeholder token, not prose.
|
||||||
|
# Covers Bash ($VAR, ${VAR}, ${VAR:-default}), Mustache/Handlebars/Jinja
|
||||||
|
# ({{var}}, {{{var}}}), Liquid/Jinja control ({% ... %}), Python str.format /
|
||||||
|
# .NET ({var}, {0}), angle-bracket (<var>), and Windows-style (%VAR%).
|
||||||
|
# Anchored to the FULL STRING so legitimate prose mentioning a placeholder
|
||||||
|
# (e.g. "the {{magic}} of placeholders") is not flagged. The Liquid pattern
|
||||||
|
# is anchored to the START so multi-tag templates fire while mid-sentence
|
||||||
|
# {%-quotation does not.
|
||||||
|
_PLACEHOLDER_TOKEN_PATTERNS = (
|
||||||
|
re.compile(r"^\$\w+$"), # $ARGUMENTS, $args
|
||||||
|
re.compile(r"^\$\{\w+(?:[:\-+?][^}]*)?\}$"), # ${ARGS}, ${ARGS:-default}
|
||||||
|
re.compile(r"^\{\{\{?\s*\w+(\s*[|.][^}]*)?\s*\}?\}\}$"), # {{var}} {{{var}}} {{x|y}}
|
||||||
|
re.compile(r"^\{%"), # {% if x %}{{ x }}{% endif %}
|
||||||
|
re.compile(r"^<\w+>$"), # <args>
|
||||||
|
re.compile(r"^%\w+%$"), # %USERNAME%
|
||||||
|
re.compile(r"^\{(?:\d+|[a-zA-Z_]\w*)(?:[.\[][^}]*)?(?:![rsa])?(?::[^}]*)?\}$"), # {0}, {var}, {0:>5}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _looks_like_placeholder_token(value: str) -> bool:
|
||||||
|
"""Return True if *value* matches a known placeholder-token shape."""
|
||||||
|
if not value:
|
||||||
|
return False
|
||||||
|
return any(p.search(value) for p in _PLACEHOLDER_TOKEN_PATTERNS)
|
||||||
|
|
||||||
|
|
||||||
class TestKiroCliIntegration(MarkdownIntegrationTests):
|
class TestKiroCliIntegration(MarkdownIntegrationTests):
|
||||||
KEY = "kiro-cli"
|
KEY = "kiro-cli"
|
||||||
FOLDER = ".kiro/"
|
FOLDER = ".kiro/"
|
||||||
@@ -12,6 +43,85 @@ class TestKiroCliIntegration(MarkdownIntegrationTests):
|
|||||||
REGISTRAR_DIR = ".kiro/prompts"
|
REGISTRAR_DIR = ".kiro/prompts"
|
||||||
CONTEXT_FILE = "AGENTS.md"
|
CONTEXT_FILE = "AGENTS.md"
|
||||||
|
|
||||||
|
def test_registrar_config(self):
|
||||||
|
"""Override base assertion: kiro-cli uses a prose fallback for args
|
||||||
|
because Kiro CLI file-based prompts do not natively substitute
|
||||||
|
``$ARGUMENTS`` (see issue #1926 / kirodotdev/Kiro#4141). The
|
||||||
|
regression-guard load is carried by the two layer tests below
|
||||||
|
(exact-fallback + placeholder-shape rejection)."""
|
||||||
|
i = get_integration(self.KEY)
|
||||||
|
assert i.registrar_config["dir"] == self.REGISTRAR_DIR
|
||||||
|
assert i.registrar_config["format"] == "markdown"
|
||||||
|
assert i.registrar_config["extension"] == ".md"
|
||||||
|
|
||||||
|
def test_registrar_config_args_is_exact_prose_fallback(self):
|
||||||
|
"""Layer 1 — pin the exact fallback so wording drift requires a
|
||||||
|
deliberate paired commit (production constant + test update)."""
|
||||||
|
i = get_integration(self.KEY)
|
||||||
|
assert i.registrar_config["args"] == _KIRO_ARG_FALLBACK, (
|
||||||
|
f"args drifted from the pinned fallback constant. "
|
||||||
|
f"Got: {i.registrar_config['args']!r}; expected: {_KIRO_ARG_FALLBACK!r}. "
|
||||||
|
f"If the wording change is intentional, update _KIRO_ARG_FALLBACK and "
|
||||||
|
f"this test together."
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_registrar_config_args_does_not_look_like_a_placeholder_token(self):
|
||||||
|
"""Layer 2 — independent regression guard: even if someone bypasses
|
||||||
|
layer-1 by changing both constant and test, the value still must not
|
||||||
|
look like ANY placeholder token shape ($X, ${X}, {{X}}, <X>, %X%, {0},
|
||||||
|
{% %}). Catches the class of regression Copilot called out: a swap
|
||||||
|
from $ARGUMENTS to $INPUT or {{userMessage}} would fail this test
|
||||||
|
even if it accidentally passed layer 1."""
|
||||||
|
i = get_integration(self.KEY)
|
||||||
|
args = i.registrar_config["args"]
|
||||||
|
assert not _looks_like_placeholder_token(args), (
|
||||||
|
f"registrar_config['args'] = {args!r} matches a known placeholder-"
|
||||||
|
f"token shape — Kiro CLI does not substitute placeholders so this "
|
||||||
|
f"would reach the model verbatim and break the prompt (issue #1926). "
|
||||||
|
f"Use a prose fallback instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rendered_prompts_do_not_contain_raw_arguments(self, tmp_path):
|
||||||
|
"""Rendered Kiro prompt files must NOT contain the raw ``$ARGUMENTS``
|
||||||
|
token — Kiro CLI does not substitute it, so the literal would reach
|
||||||
|
the model and break the prompt (issue #1926)."""
|
||||||
|
integration = get_integration(self.KEY)
|
||||||
|
manifest = IntegrationManifest(self.KEY, tmp_path)
|
||||||
|
integration.setup(tmp_path, manifest, script_type="sh")
|
||||||
|
|
||||||
|
prompts_dir = tmp_path / self.REGISTRAR_DIR
|
||||||
|
rendered = list(prompts_dir.glob("*.md"))
|
||||||
|
assert rendered, "expected at least one rendered prompt file"
|
||||||
|
|
||||||
|
offenders = [
|
||||||
|
p.name for p in rendered if "$ARGUMENTS" in p.read_text(encoding="utf-8")
|
||||||
|
]
|
||||||
|
assert offenders == [], (
|
||||||
|
f"these rendered prompts still contain the raw $ARGUMENTS token: {offenders}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rendered_prompts_contain_kiro_arg_placeholder(self, tmp_path):
|
||||||
|
"""The chosen kiro-cli args fallback string must end up in at least
|
||||||
|
one rendered prompt (proves substitution actually fired, not just
|
||||||
|
that $ARGUMENTS was removed). Imports the fallback constant directly
|
||||||
|
instead of reading the field back so the test stays independent of
|
||||||
|
the integration's own config — even if the registrar_config['args']
|
||||||
|
regresses, this test still verifies the FALLBACK STRING is in the
|
||||||
|
rendered output."""
|
||||||
|
integration = get_integration(self.KEY)
|
||||||
|
manifest = IntegrationManifest(self.KEY, tmp_path)
|
||||||
|
integration.setup(tmp_path, manifest, script_type="sh")
|
||||||
|
|
||||||
|
expected = _KIRO_ARG_FALLBACK
|
||||||
|
prompts_dir = tmp_path / self.REGISTRAR_DIR
|
||||||
|
contents = "\n".join(
|
||||||
|
p.read_text(encoding="utf-8") for p in prompts_dir.glob("*.md")
|
||||||
|
)
|
||||||
|
assert expected in contents, (
|
||||||
|
f"none of the rendered prompts contain the configured args fallback "
|
||||||
|
f"({expected!r})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestKiroAlias:
|
class TestKiroAlias:
|
||||||
"""--ai kiro alias normalizes to kiro-cli and auto-promotes."""
|
"""--ai kiro alias normalizes to kiro-cli and auto-promotes."""
|
||||||
|
|||||||
@@ -1949,7 +1949,16 @@ def install_self_test_preset(manager: PresetManager, speckit_version: str = "0.1
|
|||||||
|
|
||||||
|
|
||||||
class TestSelfTestPreset:
|
class TestSelfTestPreset:
|
||||||
"""Tests using the self-test preset that ships with the repo."""
|
"""Tests using the self-test preset that ships with the repo.
|
||||||
|
|
||||||
|
The self-test preset ships a wrap-strategy command (``speckit.wrap-test``)
|
||||||
|
without a corresponding core base layer; reconciliation deliberately
|
||||||
|
surfaces a UserWarning in that case. Tests install via
|
||||||
|
``install_self_test_preset`` (defined above), which scopes a narrow
|
||||||
|
``warnings.filterwarnings`` block to that specific message and
|
||||||
|
``UserWarning`` category — so the expected warning stays quiet without
|
||||||
|
masking unrelated warnings or real reconciliation failures.
|
||||||
|
"""
|
||||||
|
|
||||||
def test_self_test_preset_exists(self):
|
def test_self_test_preset_exists(self):
|
||||||
"""Verify the self-test preset directory and manifest exist."""
|
"""Verify the self-test preset directory and manifest exist."""
|
||||||
@@ -2237,7 +2246,12 @@ class TestInitOptions:
|
|||||||
|
|
||||||
|
|
||||||
class TestPresetSkills:
|
class TestPresetSkills:
|
||||||
"""Tests for preset skill registration and unregistration."""
|
"""Tests for preset skill registration and unregistration.
|
||||||
|
|
||||||
|
Tests that install the self-test preset use ``install_self_test_preset``
|
||||||
|
which scopes a narrow filter to the expected wrap-strategy warning.
|
||||||
|
Reconciliation failures remain audible so real regressions surface.
|
||||||
|
"""
|
||||||
|
|
||||||
def _write_init_options(self, project_dir, ai="claude", ai_skills=True, script="sh"):
|
def _write_init_options(self, project_dir, ai="claude", ai_skills=True, script="sh"):
|
||||||
from specify_cli import save_init_options
|
from specify_cli import save_init_options
|
||||||
|
|||||||
Reference in New Issue
Block a user