mirror of
https://github.com/github/spec-kit.git
synced 2026-03-17 02:43:08 +00:00
Compare commits
7 Commits
4a3234496e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c0c1446ec | ||
|
|
82b8ce4295 | ||
|
|
2cf332db1b | ||
|
|
b1650f884d | ||
|
|
23bd645054 | ||
|
|
bef9c2cb59 | ||
|
|
4f81fc298f |
@@ -158,6 +158,8 @@ 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 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
|
||||
|
||||
| Agent | Support | Notes |
|
||||
|
||||
@@ -72,13 +72,17 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
|
||||
| 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) |
|
||||
| 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) |
|
||||
| DocGuard — CDD Enforcement | Canonical-Driven Development enforcement. Generates, validates, scores, and traces project documentation against 92 automated checks with config-aware traceability, quality labels, and AI-ready fix prompts. Zero dependencies. | [spec-kit-docguard](https://github.com/raccioly/docguard) |
|
||||
| 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) |
|
||||
| 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 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) |
|
||||
| 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) |
|
||||
| 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) |
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-03-13T12:00:00Z",
|
||||
"updated_at": "2026-03-16T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
||||
"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": {
|
||||
"name": "Azure DevOps Integration",
|
||||
"id": "azure-devops",
|
||||
@@ -74,13 +105,57 @@
|
||||
"created_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 — CDD Enforcement",
|
||||
"name": "DocGuard \u2014 CDD Enforcement",
|
||||
"id": "docguard",
|
||||
"description": "Canonical-Driven Development enforcement. Generates, validates, scores, and traces project documentation against 92 automated checks. Zero dependencies.",
|
||||
"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.8.0",
|
||||
"download_url": "https://github.com/raccioly/docguard/releases/download/v0.8.0/spec-kit-docguard-v0.8.0.zip",
|
||||
"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",
|
||||
@@ -98,7 +173,7 @@
|
||||
},
|
||||
"provides": {
|
||||
"commands": 6,
|
||||
"hooks": 1
|
||||
"hooks": 3
|
||||
},
|
||||
"tags": [
|
||||
"documentation",
|
||||
@@ -108,13 +183,13 @@
|
||||
"traceability",
|
||||
"ai-agents",
|
||||
"enforcement",
|
||||
"scoring"
|
||||
"spec-kit"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-03-13T00:00:00Z",
|
||||
"updated_at": "2026-03-13T00:00:00Z"
|
||||
"updated_at": "2026-03-15T20:00:00Z"
|
||||
},
|
||||
"doctor": {
|
||||
"name": "Project Health Check",
|
||||
@@ -250,6 +325,37 @@
|
||||
"created_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": {
|
||||
"name": "Retrospective Extension",
|
||||
"id": "retrospective",
|
||||
@@ -351,7 +457,7 @@
|
||||
"understanding": {
|
||||
"name": "Understanding",
|
||||
"id": "understanding",
|
||||
"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.",
|
||||
"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.",
|
||||
"author": "Ladislav Bihari",
|
||||
"version": "3.4.0",
|
||||
"download_url": "https://github.com/Testimonial/understanding/archive/refs/tags/v3.4.0.zip",
|
||||
@@ -389,6 +495,38 @@
|
||||
"created_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": {
|
||||
"name": "V-Model Extension Pack",
|
||||
"id": "v-model",
|
||||
|
||||
@@ -168,7 +168,7 @@ if $JSON_MODE; then
|
||||
if [[ ${#docs[@]} -eq 0 ]]; then
|
||||
json_docs="[]"
|
||||
else
|
||||
json_docs=$(printf '"%s",' "${docs[@]}")
|
||||
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done)
|
||||
json_docs="[${json_docs%,}]"
|
||||
fi
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
|
||||
|
||||
@@ -161,7 +161,7 @@ has_jq() {
|
||||
}
|
||||
|
||||
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
|
||||
# Handles backslash, double-quote, and control characters (newline, tab, carriage return).
|
||||
# Handles backslash, double-quote, and JSON-required control character escapes (RFC 8259).
|
||||
json_escape() {
|
||||
local s="$1"
|
||||
s="${s//\\/\\\\}"
|
||||
@@ -169,6 +169,10 @@ json_escape() {
|
||||
s="${s//$'\n'/\\n}"
|
||||
s="${s//$'\t'/\\t}"
|
||||
s="${s//$'\r'/\\r}"
|
||||
s="${s//$'\b'/\\b}"
|
||||
s="${s//$'\f'/\\f}"
|
||||
# Strip remaining control characters (U+0000–U+001F) not individually escaped above
|
||||
s=$(printf '%s' "$s" | tr -d '\000-\007\013\016-\037')
|
||||
printf '%s' "$s"
|
||||
}
|
||||
|
||||
@@ -194,9 +198,11 @@ resolve_template() {
|
||||
if [ -d "$presets_dir" ]; then
|
||||
local registry_file="$presets_dir/.registry"
|
||||
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
||||
# Read preset IDs sorted by priority (lower number = higher precedence)
|
||||
local sorted_presets
|
||||
sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
|
||||
# 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
|
||||
# abort the function when python3 exits non-zero (e.g. invalid JSON).
|
||||
local sorted_presets=""
|
||||
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
|
||||
import json, sys, os
|
||||
try:
|
||||
with open(os.environ['SPECKIT_REGISTRY']) as f:
|
||||
@@ -206,14 +212,17 @@ try:
|
||||
print(pid)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
" 2>/dev/null)
|
||||
if [ $? -eq 0 ] && [ -n "$sorted_presets" ]; then
|
||||
while IFS= read -r preset_id; do
|
||||
local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done <<< "$sorted_presets"
|
||||
" 2>/dev/null); then
|
||||
if [ -n "$sorted_presets" ]; then
|
||||
# python3 succeeded and returned preset IDs — search in priority order
|
||||
while IFS= read -r preset_id; do
|
||||
local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done <<< "$sorted_presets"
|
||||
fi
|
||||
# python3 succeeded but registry has no presets — nothing to search
|
||||
else
|
||||
# python3 returned empty list — fall through to directory scan
|
||||
# python3 failed (missing, or registry parse error) — fall back to unordered directory scan
|
||||
for preset in "$presets_dir"/*/; do
|
||||
[ -d "$preset" ] || continue
|
||||
local candidate="$preset/templates/${template_name}.md"
|
||||
@@ -246,8 +255,9 @@ except Exception:
|
||||
local core="$base/${template_name}.md"
|
||||
[ -f "$core" ] && echo "$core" && return 0
|
||||
|
||||
# Return success with empty output so callers using set -e don't abort;
|
||||
# callers check [ -n "$TEMPLATE" ] to detect "not found".
|
||||
return 0
|
||||
# Template not found in any location.
|
||||
# Return 1 so callers can distinguish "not found" from "found".
|
||||
# Callers running under set -e should use: TEMPLATE=$(resolve_template ...) || true
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -162,17 +162,6 @@ clean_branch_name() {
|
||||
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
|
||||
# to searching for repository markers so the workflow still functions in repositories that
|
||||
# were initialised with --no-git.
|
||||
@@ -308,9 +297,14 @@ fi
|
||||
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
|
||||
mkdir -p "$FEATURE_DIR"
|
||||
|
||||
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT")
|
||||
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT") || true
|
||||
SPEC_FILE="$FEATURE_DIR/spec.md"
|
||||
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
|
||||
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then
|
||||
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
|
||||
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2
|
||||
|
||||
@@ -39,7 +39,7 @@ check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
||||
mkdir -p "$FEATURE_DIR"
|
||||
|
||||
# Copy plan template if it exists
|
||||
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT")
|
||||
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT") || true
|
||||
if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then
|
||||
cp "$TEMPLATE" "$IMPL_PLAN"
|
||||
echo "Copied plan template to $IMPL_PLAN"
|
||||
|
||||
@@ -686,56 +686,67 @@ update_specific_agent() {
|
||||
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() {
|
||||
local found_agent=false
|
||||
local _updated_paths=()
|
||||
_found_agent=false
|
||||
_updated_paths=()
|
||||
local _all_ok=true
|
||||
|
||||
# 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.
|
||||
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
|
||||
update_agent_file "$file" "$name" || return 1
|
||||
_updated_paths+=("$real_path")
|
||||
found_agent=true
|
||||
}
|
||||
|
||||
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"
|
||||
_update_if_new "$CLAUDE_FILE" "Claude Code" || _all_ok=false
|
||||
_update_if_new "$GEMINI_FILE" "Gemini CLI" || _all_ok=false
|
||||
_update_if_new "$COPILOT_FILE" "GitHub Copilot" || _all_ok=false
|
||||
_update_if_new "$CURSOR_FILE" "Cursor IDE" || _all_ok=false
|
||||
_update_if_new "$QWEN_FILE" "Qwen Code" || _all_ok=false
|
||||
_update_if_new "$AGENTS_FILE" "Codex/opencode" || _all_ok=false
|
||||
_update_if_new "$AMP_FILE" "Amp" || _all_ok=false
|
||||
_update_if_new "$KIRO_FILE" "Kiro CLI" || _all_ok=false
|
||||
_update_if_new "$BOB_FILE" "IBM Bob" || _all_ok=false
|
||||
_update_if_new "$WINDSURF_FILE" "Windsurf" || _all_ok=false
|
||||
_update_if_new "$KILOCODE_FILE" "Kilo Code" || _all_ok=false
|
||||
_update_if_new "$AUGGIE_FILE" "Auggie CLI" || _all_ok=false
|
||||
_update_if_new "$ROO_FILE" "Roo Code" || _all_ok=false
|
||||
_update_if_new "$CODEBUDDY_FILE" "CodeBuddy CLI" || _all_ok=false
|
||||
_update_if_new "$SHAI_FILE" "SHAI" || _all_ok=false
|
||||
_update_if_new "$TABNINE_FILE" "Tabnine CLI" || _all_ok=false
|
||||
_update_if_new "$QODER_FILE" "Qoder CLI" || _all_ok=false
|
||||
_update_if_new "$AGY_FILE" "Antigravity" || _all_ok=false
|
||||
_update_if_new "$VIBE_FILE" "Mistral Vibe" || _all_ok=false
|
||||
_update_if_new "$KIMI_FILE" "Kimi Code" || _all_ok=false
|
||||
|
||||
# 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..."
|
||||
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
|
||||
fi
|
||||
|
||||
[[ "$_all_ok" == true ]]
|
||||
}
|
||||
print_summary() {
|
||||
echo
|
||||
|
||||
@@ -2576,6 +2576,7 @@ def extension_list(
|
||||
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" [dim]{ext['id']}[/dim]")
|
||||
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()
|
||||
|
||||
@@ -2337,3 +2337,29 @@ class TestExtensionUpdateCLI:
|
||||
|
||||
for cmd_file in command_files:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user