Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
2656ee1df7 chore: bump version to 0.3.0 2026-03-13 20:11:58 +00:00
11 changed files with 112 additions and 327 deletions

View File

@@ -11,9 +11,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- No changes have been documented for this release yet. - feat(presets): Pluggable preset system with catalog, resolver, and skills propagation (#1787)
- fix: match 'Last updated' timestamp with or without bold markers (#1836)
<!-- Entries for 0.2.x and earlier releases are documented in their respective sections below. --> - Add specify doctor command for project health diagnostics (#1828)
- fix: harden bash scripts against shell injection and improve robustness (#1809)
- fix: clean up command templates (specify, analyze) (#1810)
- fix: migrate Qwen Code CLI from TOML to Markdown format (#1589) (#1730)
- fix(cli): deprecate explicit command support for agy (#1798) (#1808)
- Add /selftest.extension core extension to test other extensions (#1758)
- feat(extensions): Quality of life improvements for RFC-aligned catalog integration (#1776)
- Add Java brownfield walkthrough to community walkthroughs (#1820)
- chore: bump version to 0.2.1 (#1813)
- Added February 2026 newsletter (#1812)
- feat: add Kimi Code CLI agent support (#1790)
- docs: fix broken links in quickstart guide (#1759) (#1797)
- docs: add catalog cli help documentation (#1793) (#1794)
- fix: use quiet checkout to avoid exception on git checkout (#1792)
- feat(extensions): support .extensionignore to exclude files during install (#1781)
- feat: add Codex support for extension command registration (#1767)
- chore: bump version to 0.2.0 (#1786)
- fix: sync agent list comments with actual supported agents (#1785)
- feat(extensions): support multiple active catalogs simultaneously (#1720)
- Pavel/add tabnine cli support (#1503)
- Add Understanding extension to community catalog (#1778)
- Add ralph extension to community catalog (#1780)
- Update README with project initialization instructions (#1772)
- feat: add review extension to community catalog (#1775)
- Add fleet extension to community catalog (#1771)
- Integration of Mistral vibe support into speckit (#1725)
- fix: Remove duplicate options in specify.md (#1765)
- fix: use global branch numbering instead of per-short-name detection (#1757)
- Add Community Walkthroughs section to README (#1766)
- feat(extensions): add Jira Integration to community catalog (#1764)
- Add Azure DevOps Integration extension to community catalog (#1734)
- Fix docs: update Antigravity link and add initialization example (#1748)
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
- make c ignores consistent with c++ (#1747) - make c ignores consistent with c++ (#1747)
- chore: bump version to 0.1.13 (#1746) - chore: bump version to 0.1.13 (#1746)
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690) - feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)

View File

@@ -158,8 +158,6 @@ See Spec-Driven Development in action across different scenarios with these comm
- **[Brownfield Java runtime extension](https://github.com/mnriem/spec-kit-java-brownfield-demo)** — Extends an existing open-source Jakarta EE runtime (Piranha, ~420,000 lines of Java, XML, JSP, HTML, and config files across 180 Maven modules) with a password-protected Server Admin Console, demonstrating spec-kit on a large multi-module Java project with no prior specs or constitution. - **[Brownfield Java runtime extension](https://github.com/mnriem/spec-kit-java-brownfield-demo)** — Extends an existing open-source Jakarta EE runtime (Piranha, ~420,000 lines of Java, XML, JSP, HTML, and config files across 180 Maven modules) with a password-protected Server Admin Console, demonstrating spec-kit on a large multi-module Java project with no prior specs or constitution.
- **[Brownfield Go / React dashboard demo](https://github.com/mnriem/spec-kit-go-brownfield-demo)** — Demonstrates spec-kit driven entirely from the **terminal using GitHub Copilot CLI**. Extends NASA's open-source Hermes ground support system (Go) with a lightweight React-based web telemetry dashboard, showing that the full constitution → specify → plan → tasks → implement workflow works from the terminal.
## 🤖 Supported AI Agents ## 🤖 Supported AI Agents
| Agent | Support | Notes | | Agent | Support | Notes |

View File

@@ -72,17 +72,12 @@ The following community-contributed extensions are available in [`catalog.commun
| Extension | Purpose | URL | | Extension | Purpose | URL |
|-----------|---------|-----| |-----------|---------|-----|
| Archive Extension | Archive merged features into main project memory. | [spec-kit-archive](https://github.com/stn1slv/spec-kit-archive) |
| Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) | | Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) |
| Cleanup Extension | Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) | | Cleanup Extension | Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) |
| Cognitive Squad | 19-function cognitive agent squad for autonomous pre-code analysis — 7 core agents, 7 specialists, 4 learning functions with feedback loop | [cognitive-squad](https://github.com/Testimonial/cognitive-squad) |
| DocGuard — CDD Enforcement | Canonical-Driven Development enforcement. Validates, scores, and traces project documentation with automated checks, AI-driven workflows, and spec-kit hooks. Zero dependencies. | [spec-kit-docguard](https://github.com/raccioly/docguard) |
| Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) | | Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) |
| Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) | | Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) |
| Project Health Check | Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git | [spec-kit-doctor](https://github.com/KhawarHabibKhan/spec-kit-doctor) | | Project Health Check | Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git | [spec-kit-doctor](https://github.com/KhawarHabibKhan/spec-kit-doctor) |
| Project Status | Show current SDD workflow progress — active feature, artifact status, task completion, workflow phase, and extensions summary | [spec-kit-status](https://github.com/KhawarHabibKhan/spec-kit-status) |
| Ralph Loop | Autonomous implementation loop using AI agent CLI | [spec-kit-ralph](https://github.com/Rubiss/spec-kit-ralph) | | Ralph Loop | Autonomous implementation loop using AI agent CLI | [spec-kit-ralph](https://github.com/Rubiss/spec-kit-ralph) |
| Reconcile Extension | Reconcile implementation drift by surgically updating feature artifacts. | [spec-kit-reconcile](https://github.com/stn1slv/spec-kit-reconcile) |
| Retrospective Extension | Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates | [spec-kit-retrospective](https://github.com/emi-dm/spec-kit-retrospective) | | Retrospective Extension | Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates | [spec-kit-retrospective](https://github.com/emi-dm/spec-kit-retrospective) |
| Review Extension | Post-implementation comprehensive code review with specialized agents for code quality, comments, tests, error handling, type design, and simplification | [spec-kit-review](https://github.com/ismaelJimenez/spec-kit-review) | | Review Extension | Post-implementation comprehensive code review with specialized agents for code quality, comments, tests, error handling, type design, and simplification | [spec-kit-review](https://github.com/ismaelJimenez/spec-kit-review) |
| Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | [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 | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) |

View File

@@ -1,39 +1,8 @@
{ {
"schema_version": "1.0", "schema_version": "1.0",
"updated_at": "2026-03-16T00:00:00Z", "updated_at": "2026-03-13T12:00:00Z",
"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": {
"archive": {
"name": "Archive Extension",
"id": "archive",
"description": "Archive merged features into main project memory, resolving gaps and conflicts.",
"author": "Stanislav Deviatov",
"version": "1.0.0",
"download_url": "https://github.com/stn1slv/spec-kit-archive/archive/refs/tags/v1.0.0.zip",
"repository": "https://github.com/stn1slv/spec-kit-archive",
"homepage": "https://github.com/stn1slv/spec-kit-archive",
"documentation": "https://github.com/stn1slv/spec-kit-archive/blob/main/README.md",
"changelog": "https://github.com/stn1slv/spec-kit-archive/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0"
},
"provides": {
"commands": 1,
"hooks": 0
},
"tags": [
"archive",
"memory",
"merge",
"changelog"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-03-14T00:00:00Z",
"updated_at": "2026-03-14T00:00:00Z"
},
"azure-devops": { "azure-devops": {
"name": "Azure DevOps Integration", "name": "Azure DevOps Integration",
"id": "azure-devops", "id": "azure-devops",
@@ -105,92 +74,6 @@
"created_at": "2026-02-22T00:00:00Z", "created_at": "2026-02-22T00:00:00Z",
"updated_at": "2026-02-22T00:00:00Z" "updated_at": "2026-02-22T00:00:00Z"
}, },
"cognitive-squad": {
"name": "Cognitive Squad",
"id": "cognitive-squad",
"description": "19-function cognitive agent squad for autonomous pre-code analysis — 7 core agents, 7 specialists, 4 learning functions with feedback loop",
"author": "Testimonial",
"version": "0.1.0",
"download_url": "https://github.com/Testimonial/cognitive-squad/archive/refs/tags/v0.1.0.zip",
"repository": "https://github.com/Testimonial/cognitive-squad",
"homepage": "https://github.com/Testimonial/cognitive-squad",
"documentation": "https://github.com/Testimonial/cognitive-squad/blob/main/README.md",
"changelog": "https://github.com/Testimonial/cognitive-squad/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.3.0",
"tools": [
{
"name": "understanding",
"version": ">=3.4.0",
"required": false
},
{
"name": "spec-kit-reverse-eng",
"version": ">=1.0.0",
"required": false
}
]
},
"provides": {
"commands": 7,
"hooks": 1
},
"tags": [
"ai-agents",
"cognitive",
"pre-code",
"analysis",
"multi-agent"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-03-16T00:00:00Z",
"updated_at": "2026-03-16T00:00:00Z"
},
"docguard": {
"name": "DocGuard \u2014 CDD Enforcement",
"id": "docguard",
"description": "Canonical-Driven Development enforcement. Validates, scores, and traces project documentation with automated checks, AI-driven workflows, and spec-kit hooks. Zero dependencies.",
"author": "raccioly",
"version": "0.9.8",
"download_url": "https://github.com/raccioly/docguard/releases/download/v0.9.8/spec-kit-docguard-v0.9.8.zip",
"repository": "https://github.com/raccioly/docguard",
"homepage": "https://www.npmjs.com/package/docguard-cli",
"documentation": "https://github.com/raccioly/docguard/blob/main/extensions/spec-kit-docguard/README.md",
"changelog": "https://github.com/raccioly/docguard/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
{
"name": "node",
"version": ">=18.0.0",
"required": true
}
]
},
"provides": {
"commands": 6,
"hooks": 3
},
"tags": [
"documentation",
"validation",
"quality",
"cdd",
"traceability",
"ai-agents",
"enforcement",
"spec-kit"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-03-13T00:00:00Z",
"updated_at": "2026-03-15T20:00:00Z"
},
"doctor": { "doctor": {
"name": "Project Health Check", "name": "Project Health Check",
"id": "doctor", "id": "doctor",
@@ -241,12 +124,7 @@
"commands": 2, "commands": 2,
"hooks": 1 "hooks": 1
}, },
"tags": [ "tags": ["orchestration", "workflow", "human-in-the-loop", "parallel"],
"orchestration",
"workflow",
"human-in-the-loop",
"parallel"
],
"verified": false, "verified": false,
"downloads": 0, "downloads": 0,
"stars": 0, "stars": 0,
@@ -313,49 +191,13 @@
"commands": 2, "commands": 2,
"hooks": 1 "hooks": 1
}, },
"tags": [ "tags": ["implementation", "automation", "loop", "copilot"],
"implementation",
"automation",
"loop",
"copilot"
],
"verified": false, "verified": false,
"downloads": 0, "downloads": 0,
"stars": 0, "stars": 0,
"created_at": "2026-03-09T00:00:00Z", "created_at": "2026-03-09T00:00:00Z",
"updated_at": "2026-03-09T00:00:00Z" "updated_at": "2026-03-09T00:00:00Z"
}, },
"reconcile": {
"name": "Reconcile Extension",
"id": "reconcile",
"description": "Reconcile implementation drift by surgically updating the feature's own spec, plan, and tasks.",
"author": "Stanislav Deviatov",
"version": "1.0.0",
"download_url": "https://github.com/stn1slv/spec-kit-reconcile/archive/refs/tags/v1.0.0.zip",
"repository": "https://github.com/stn1slv/spec-kit-reconcile",
"homepage": "https://github.com/stn1slv/spec-kit-reconcile",
"documentation": "https://github.com/stn1slv/spec-kit-reconcile/blob/main/README.md",
"changelog": "https://github.com/stn1slv/spec-kit-reconcile/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0"
},
"provides": {
"commands": 1,
"hooks": 0
},
"tags": [
"reconcile",
"drift",
"tasks",
"remediation"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-03-14T00:00:00Z",
"updated_at": "2026-03-14T00:00:00Z"
},
"retrospective": { "retrospective": {
"name": "Retrospective Extension", "name": "Retrospective Extension",
"id": "retrospective", "id": "retrospective",
@@ -407,15 +249,7 @@
"commands": 7, "commands": 7,
"hooks": 1 "hooks": 1
}, },
"tags": [ "tags": ["code-review", "quality", "review", "testing", "error-handling", "type-design", "simplification"],
"code-review",
"quality",
"review",
"testing",
"error-handling",
"type-design",
"simplification"
],
"verified": false, "verified": false,
"downloads": 0, "downloads": 0,
"stars": 0, "stars": 0,
@@ -457,7 +291,7 @@
"understanding": { "understanding": {
"name": "Understanding", "name": "Understanding",
"id": "understanding", "id": "understanding",
"description": "Automated requirements quality analysis \u2014 validates specs against IEEE/ISO standards using 31 deterministic metrics. Catches ambiguity, missing testability, and structural issues before they reach implementation. Includes experimental energy-based ambiguity detection using local LM token perplexity.", "description": "Automated requirements quality analysis validates specs against IEEE/ISO standards using 31 deterministic metrics. Catches ambiguity, missing testability, and structural issues before they reach implementation. Includes experimental energy-based ambiguity detection using local LM token perplexity.",
"author": "Ladislav Bihari", "author": "Ladislav Bihari",
"version": "3.4.0", "version": "3.4.0",
"download_url": "https://github.com/Testimonial/understanding/archive/refs/tags/v3.4.0.zip", "download_url": "https://github.com/Testimonial/understanding/archive/refs/tags/v3.4.0.zip",
@@ -495,38 +329,6 @@
"created_at": "2026-03-07T00:00:00Z", "created_at": "2026-03-07T00:00:00Z",
"updated_at": "2026-03-07T00:00:00Z" "updated_at": "2026-03-07T00:00:00Z"
}, },
"status": {
"name": "Project Status",
"id": "status",
"description": "Show current SDD workflow progress — active feature, artifact status, task completion, workflow phase, and extensions summary.",
"author": "KhawarHabibKhan",
"version": "1.0.0",
"download_url": "https://github.com/KhawarHabibKhan/spec-kit-status/archive/refs/tags/v1.0.0.zip",
"repository": "https://github.com/KhawarHabibKhan/spec-kit-status",
"homepage": "https://github.com/KhawarHabibKhan/spec-kit-status",
"documentation": "https://github.com/KhawarHabibKhan/spec-kit-status/blob/main/README.md",
"changelog": "https://github.com/KhawarHabibKhan/spec-kit-status/blob/main/CHANGELOG.md",
"license": "MIT",
"requires": {
"speckit_version": ">=0.1.0"
},
"provides": {
"commands": 1,
"hooks": 0
},
"tags": [
"status",
"workflow",
"progress",
"feature-tracking",
"task-progress"
],
"verified": false,
"downloads": 0,
"stars": 0,
"created_at": "2026-03-16T00:00:00Z",
"updated_at": "2026-03-16T00:00:00Z"
},
"v-model": { "v-model": {
"name": "V-Model Extension Pack", "name": "V-Model Extension Pack",
"id": "v-model", "id": "v-model",

View File

@@ -168,7 +168,7 @@ if $JSON_MODE; then
if [[ ${#docs[@]} -eq 0 ]]; then if [[ ${#docs[@]} -eq 0 ]]; then
json_docs="[]" json_docs="[]"
else else
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done) json_docs=$(printf '"%s",' "${docs[@]}")
json_docs="[${json_docs%,}]" json_docs="[${json_docs%,}]"
fi fi
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs" printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"

View File

@@ -161,7 +161,7 @@ has_jq() {
} }
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable). # Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
# Handles backslash, double-quote, and JSON-required control character escapes (RFC 8259). # Handles backslash, double-quote, and control characters (newline, tab, carriage return).
json_escape() { json_escape() {
local s="$1" local s="$1"
s="${s//\\/\\\\}" s="${s//\\/\\\\}"
@@ -169,10 +169,6 @@ json_escape() {
s="${s//$'\n'/\\n}" s="${s//$'\n'/\\n}"
s="${s//$'\t'/\\t}" s="${s//$'\t'/\\t}"
s="${s//$'\r'/\\r}" s="${s//$'\r'/\\r}"
s="${s//$'\b'/\\b}"
s="${s//$'\f'/\\f}"
# Strip remaining control characters (U+0000U+001F) not individually escaped above
s=$(printf '%s' "$s" | tr -d '\000-\007\013\016-\037')
printf '%s' "$s" printf '%s' "$s"
} }
@@ -198,11 +194,9 @@ resolve_template() {
if [ -d "$presets_dir" ]; then if [ -d "$presets_dir" ]; then
local registry_file="$presets_dir/.registry" local registry_file="$presets_dir/.registry"
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
# Read preset IDs sorted by priority (lower number = higher precedence). # Read preset IDs sorted by priority (lower number = higher precedence)
# The python3 call is wrapped in an if-condition so that set -e does not local sorted_presets
# abort the function when python3 exits non-zero (e.g. invalid JSON). sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
local sorted_presets=""
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
import json, sys, os import json, sys, os
try: try:
with open(os.environ['SPECKIT_REGISTRY']) as f: with open(os.environ['SPECKIT_REGISTRY']) as f:
@@ -212,17 +206,14 @@ try:
print(pid) print(pid)
except Exception: except Exception:
sys.exit(1) sys.exit(1)
" 2>/dev/null); then " 2>/dev/null)
if [ -n "$sorted_presets" ]; then if [ $? -eq 0 ] && [ -n "$sorted_presets" ]; then
# python3 succeeded and returned preset IDs — search in priority order while IFS= read -r preset_id; do
while IFS= read -r preset_id; do local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
local candidate="$presets_dir/$preset_id/templates/${template_name}.md" [ -f "$candidate" ] && echo "$candidate" && return 0
[ -f "$candidate" ] && echo "$candidate" && return 0 done <<< "$sorted_presets"
done <<< "$sorted_presets"
fi
# python3 succeeded but registry has no presets — nothing to search
else else
# python3 failed (missing, or registry parse error) — fall back to unordered directory scan # python3 returned empty list — fall through to directory scan
for preset in "$presets_dir"/*/; do for preset in "$presets_dir"/*/; do
[ -d "$preset" ] || continue [ -d "$preset" ] || continue
local candidate="$preset/templates/${template_name}.md" local candidate="$preset/templates/${template_name}.md"
@@ -255,9 +246,8 @@ except Exception:
local core="$base/${template_name}.md" local core="$base/${template_name}.md"
[ -f "$core" ] && echo "$core" && return 0 [ -f "$core" ] && echo "$core" && return 0
# Template not found in any location. # Return success with empty output so callers using set -e don't abort;
# Return 1 so callers can distinguish "not found" from "found". # callers check [ -n "$TEMPLATE" ] to detect "not found".
# Callers running under set -e should use: TEMPLATE=$(resolve_template ...) || true return 0
return 1
} }

View File

@@ -162,6 +162,17 @@ clean_branch_name() {
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//' echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
} }
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
json_escape() {
local s="$1"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//$'\n'/\\n}"
s="${s//$'\t'/\\t}"
s="${s//$'\r'/\\r}"
printf '%s' "$s"
}
# Resolve repository root. Prefer git information when available, but fall back # Resolve repository root. Prefer git information when available, but fall back
# to searching for repository markers so the workflow still functions in repositories that # to searching for repository markers so the workflow still functions in repositories that
# were initialised with --no-git. # were initialised with --no-git.
@@ -297,14 +308,9 @@ fi
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME" FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
mkdir -p "$FEATURE_DIR" mkdir -p "$FEATURE_DIR"
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT") || true TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT")
SPEC_FILE="$FEATURE_DIR/spec.md" SPEC_FILE="$FEATURE_DIR/spec.md"
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
cp "$TEMPLATE" "$SPEC_FILE"
else
echo "Warning: Spec template not found; created empty spec file" >&2
touch "$SPEC_FILE"
fi
# Inform the user how to persist the feature variable in their own shell # Inform the user how to persist the feature variable in their own shell
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2 printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2

View File

@@ -39,7 +39,7 @@ check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
mkdir -p "$FEATURE_DIR" mkdir -p "$FEATURE_DIR"
# Copy plan template if it exists # Copy plan template if it exists
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT") || true TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT")
if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then
cp "$TEMPLATE" "$IMPL_PLAN" cp "$TEMPLATE" "$IMPL_PLAN"
echo "Copied plan template to $IMPL_PLAN" echo "Copied plan template to $IMPL_PLAN"

View File

@@ -686,67 +686,56 @@ update_specific_agent() {
esac esac
} }
# Helper: skip non-existent files and files already updated (dedup by
# realpath so that variables pointing to the same file — e.g. AMP_FILE,
# KIRO_FILE, BOB_FILE all resolving to AGENTS_FILE — are only written once).
# Uses a linear array instead of associative array for bash 3.2 compatibility.
# Note: defined at top level because bash 3.2 does not support true
# nested/local functions. _updated_paths, _found_agent, and _all_ok are
# initialised exclusively inside update_all_existing_agents so that
# sourcing this script has no side effects on the caller's environment.
_update_if_new() {
local file="$1" name="$2"
[[ -f "$file" ]] || return 0
local real_path
real_path=$(realpath "$file" 2>/dev/null || echo "$file")
local p
if [[ ${#_updated_paths[@]} -gt 0 ]]; then
for p in "${_updated_paths[@]}"; do
[[ "$p" == "$real_path" ]] && return 0
done
fi
# Record the file as seen before attempting the update so that:
# (a) aliases pointing to the same path are not retried on failure
# (b) _found_agent reflects file existence, not update success
_updated_paths+=("$real_path")
_found_agent=true
update_agent_file "$file" "$name"
}
update_all_existing_agents() { update_all_existing_agents() {
_found_agent=false local found_agent=false
_updated_paths=() local _updated_paths=()
local _all_ok=true
_update_if_new "$CLAUDE_FILE" "Claude Code" || _all_ok=false # Helper: skip non-existent files and files already updated (dedup by
_update_if_new "$GEMINI_FILE" "Gemini CLI" || _all_ok=false # realpath so that variables pointing to the same file — e.g. AMP_FILE,
_update_if_new "$COPILOT_FILE" "GitHub Copilot" || _all_ok=false # KIRO_FILE, BOB_FILE all resolving to AGENTS_FILE — are only written once).
_update_if_new "$CURSOR_FILE" "Cursor IDE" || _all_ok=false # Uses a linear array instead of associative array for bash 3.2 compatibility.
_update_if_new "$QWEN_FILE" "Qwen Code" || _all_ok=false update_if_new() {
_update_if_new "$AGENTS_FILE" "Codex/opencode" || _all_ok=false local file="$1" name="$2"
_update_if_new "$AMP_FILE" "Amp" || _all_ok=false [[ -f "$file" ]] || return 0
_update_if_new "$KIRO_FILE" "Kiro CLI" || _all_ok=false local real_path
_update_if_new "$BOB_FILE" "IBM Bob" || _all_ok=false real_path=$(realpath "$file" 2>/dev/null || echo "$file")
_update_if_new "$WINDSURF_FILE" "Windsurf" || _all_ok=false local p
_update_if_new "$KILOCODE_FILE" "Kilo Code" || _all_ok=false if [[ ${#_updated_paths[@]} -gt 0 ]]; then
_update_if_new "$AUGGIE_FILE" "Auggie CLI" || _all_ok=false for p in "${_updated_paths[@]}"; do
_update_if_new "$ROO_FILE" "Roo Code" || _all_ok=false [[ "$p" == "$real_path" ]] && return 0
_update_if_new "$CODEBUDDY_FILE" "CodeBuddy CLI" || _all_ok=false done
_update_if_new "$SHAI_FILE" "SHAI" || _all_ok=false fi
_update_if_new "$TABNINE_FILE" "Tabnine CLI" || _all_ok=false update_agent_file "$file" "$name" || return 1
_update_if_new "$QODER_FILE" "Qoder CLI" || _all_ok=false _updated_paths+=("$real_path")
_update_if_new "$AGY_FILE" "Antigravity" || _all_ok=false found_agent=true
_update_if_new "$VIBE_FILE" "Mistral Vibe" || _all_ok=false }
_update_if_new "$KIMI_FILE" "Kimi Code" || _all_ok=false
update_if_new "$CLAUDE_FILE" "Claude Code"
update_if_new "$GEMINI_FILE" "Gemini CLI"
update_if_new "$COPILOT_FILE" "GitHub Copilot"
update_if_new "$CURSOR_FILE" "Cursor IDE"
update_if_new "$QWEN_FILE" "Qwen Code"
update_if_new "$AGENTS_FILE" "Codex/opencode"
update_if_new "$AMP_FILE" "Amp"
update_if_new "$KIRO_FILE" "Kiro CLI"
update_if_new "$BOB_FILE" "IBM Bob"
update_if_new "$WINDSURF_FILE" "Windsurf"
update_if_new "$KILOCODE_FILE" "Kilo Code"
update_if_new "$AUGGIE_FILE" "Auggie CLI"
update_if_new "$ROO_FILE" "Roo Code"
update_if_new "$CODEBUDDY_FILE" "CodeBuddy CLI"
update_if_new "$SHAI_FILE" "SHAI"
update_if_new "$TABNINE_FILE" "Tabnine CLI"
update_if_new "$QODER_FILE" "Qoder CLI"
update_if_new "$AGY_FILE" "Antigravity"
update_if_new "$VIBE_FILE" "Mistral Vibe"
update_if_new "$KIMI_FILE" "Kimi Code"
# If no agent files exist, create a default Claude file # If no agent files exist, create a default Claude file
if [[ "$_found_agent" == false ]]; then if [[ "$found_agent" == false ]]; then
log_info "No existing agent files found, creating default Claude file..." log_info "No existing agent files found, creating default Claude file..."
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1 update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
fi fi
[[ "$_all_ok" == true ]]
} }
print_summary() { print_summary() {
echo echo

View File

@@ -2576,7 +2576,6 @@ def extension_list(
status_color = "green" if ext["enabled"] else "red" status_color = "green" if ext["enabled"] else "red"
console.print(f" [{status_color}]{status_icon}[/{status_color}] [bold]{ext['name']}[/bold] (v{ext['version']})") console.print(f" [{status_color}]{status_icon}[/{status_color}] [bold]{ext['name']}[/bold] (v{ext['version']})")
console.print(f" [dim]{ext['id']}[/dim]")
console.print(f" {ext['description']}") console.print(f" {ext['description']}")
console.print(f" Commands: {ext['command_count']} | Hooks: {ext['hook_count']} | Status: {'Enabled' if ext['enabled'] else 'Disabled'}") console.print(f" Commands: {ext['command_count']} | Hooks: {ext['hook_count']} | Status: {'Enabled' if ext['enabled'] else 'Disabled'}")
console.print() console.print()

View File

@@ -2337,29 +2337,3 @@ class TestExtensionUpdateCLI:
for cmd_file in command_files: for cmd_file in command_files:
assert cmd_file.exists(), f"Expected command file to be restored after rollback: {cmd_file}" assert cmd_file.exists(), f"Expected command file to be restored after rollback: {cmd_file}"
class TestExtensionListCLI:
"""Test extension list CLI output format."""
def test_list_shows_extension_id(self, extension_dir, project_dir):
"""extension list should display the extension ID."""
from typer.testing import CliRunner
from unittest.mock import patch
from specify_cli import app
runner = CliRunner()
# Install the extension using the manager
manager = ExtensionManager(project_dir)
manager.install_from_directory(extension_dir, "0.1.0", register_commands=False)
with patch.object(Path, "cwd", return_value=project_dir):
result = runner.invoke(app, ["extension", "list"])
assert result.exit_code == 0, result.output
# Verify the extension ID is shown in the output
assert "test-ext" in result.output
# Verify name and version are also shown
assert "Test Extension" in result.output
assert "1.0.0" in result.output