Compare commits

..

1 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
52394c6447 Initial plan 2025-10-22 03:26:21 +00:00
24 changed files with 200 additions and 1383 deletions

View File

@@ -40,15 +40,9 @@ gh release create "$VERSION" \
.genreleases/spec-kit-template-roo-ps-"$VERSION".zip \ .genreleases/spec-kit-template-roo-ps-"$VERSION".zip \
.genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \ .genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \
.genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \ .genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \
.genreleases/spec-kit-template-qoder-sh-"$VERSION".zip \
.genreleases/spec-kit-template-qoder-ps-"$VERSION".zip \
.genreleases/spec-kit-template-amp-sh-"$VERSION".zip \ .genreleases/spec-kit-template-amp-sh-"$VERSION".zip \
.genreleases/spec-kit-template-amp-ps-"$VERSION".zip \ .genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
.genreleases/spec-kit-template-shai-sh-"$VERSION".zip \
.genreleases/spec-kit-template-shai-ps-"$VERSION".zip \
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \ .genreleases/spec-kit-template-q-sh-"$VERSION".zip \
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \ .genreleases/spec-kit-template-q-ps-"$VERSION".zip \
.genreleases/spec-kit-template-bob-sh-"$VERSION".zip \
.genreleases/spec-kit-template-bob-ps-"$VERSION".zip \
--title "Spec Kit Templates - $VERSION_NO_V" \ --title "Spec Kit Templates - $VERSION_NO_V" \
--notes-file release_notes.md --notes-file release_notes.md

View File

@@ -1,424 +0,0 @@
#!/usr/bin/env pwsh
#requires -Version 7.0
<#
.SYNOPSIS
Build Spec Kit template release archives for each supported AI assistant and script type.
.DESCRIPTION
create-release-packages.ps1 (workflow-local)
Build Spec Kit template release archives for each supported AI assistant and script type.
.PARAMETER Version
Version string with leading 'v' (e.g., v0.2.0)
.PARAMETER Agents
Comma or space separated subset of agents to build (default: all)
Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, q, bob, qoder
.PARAMETER Scripts
Comma or space separated subset of script types to build (default: both)
Valid scripts: sh, ps
.EXAMPLE
.\create-release-packages.ps1 -Version v0.2.0
.EXAMPLE
.\create-release-packages.ps1 -Version v0.2.0 -Agents claude,copilot -Scripts sh
.EXAMPLE
.\create-release-packages.ps1 -Version v0.2.0 -Agents claude -Scripts ps
#>
param(
[Parameter(Mandatory=$true, Position=0)]
[string]$Version,
[Parameter(Mandatory=$false)]
[string]$Agents = "",
[Parameter(Mandatory=$false)]
[string]$Scripts = ""
)
$ErrorActionPreference = "Stop"
# Validate version format
if ($Version -notmatch '^v\d+\.\d+\.\d+$') {
Write-Error "Version must look like v0.0.0"
exit 1
}
Write-Host "Building release packages for $Version"
# Create and use .genreleases directory for all build artifacts
$GenReleasesDir = ".genreleases"
if (Test-Path $GenReleasesDir) {
Remove-Item -Path $GenReleasesDir -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Path $GenReleasesDir -Force | Out-Null
function Rewrite-Paths {
param([string]$Content)
$Content = $Content -replace '(/?)\bmemory/', '.specify/memory/'
$Content = $Content -replace '(/?)\bscripts/', '.specify/scripts/'
$Content = $Content -replace '(/?)\btemplates/', '.specify/templates/'
return $Content
}
function Generate-Commands {
param(
[string]$Agent,
[string]$Extension,
[string]$ArgFormat,
[string]$OutputDir,
[string]$ScriptVariant
)
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
$templates = Get-ChildItem -Path "templates/commands/*.md" -File -ErrorAction SilentlyContinue
foreach ($template in $templates) {
$name = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)
# Read file content and normalize line endings
$fileContent = (Get-Content -Path $template.FullName -Raw) -replace "`r`n", "`n"
# Extract description from YAML frontmatter
$description = ""
if ($fileContent -match '(?m)^description:\s*(.+)$') {
$description = $matches[1]
}
# Extract script command from YAML frontmatter
$scriptCommand = ""
if ($fileContent -match "(?m)^\s*${ScriptVariant}:\s*(.+)$") {
$scriptCommand = $matches[1]
}
if ([string]::IsNullOrEmpty($scriptCommand)) {
Write-Warning "No script command found for $ScriptVariant in $($template.Name)"
$scriptCommand = "(Missing script command for $ScriptVariant)"
}
# Extract agent_script command from YAML frontmatter if present
$agentScriptCommand = ""
if ($fileContent -match "(?ms)agent_scripts:.*?^\s*${ScriptVariant}:\s*(.+?)$") {
$agentScriptCommand = $matches[1].Trim()
}
# Replace {SCRIPT} placeholder with the script command
$body = $fileContent -replace '\{SCRIPT\}', $scriptCommand
# Replace {AGENT_SCRIPT} placeholder with the agent script command if found
if (-not [string]::IsNullOrEmpty($agentScriptCommand)) {
$body = $body -replace '\{AGENT_SCRIPT\}', $agentScriptCommand
}
# Remove the scripts: and agent_scripts: sections from frontmatter
$lines = $body -split "`n"
$outputLines = @()
$inFrontmatter = $false
$skipScripts = $false
$dashCount = 0
foreach ($line in $lines) {
if ($line -match '^---$') {
$outputLines += $line
$dashCount++
if ($dashCount -eq 1) {
$inFrontmatter = $true
} else {
$inFrontmatter = $false
}
continue
}
if ($inFrontmatter) {
if ($line -match '^(scripts|agent_scripts):$') {
$skipScripts = $true
continue
}
if ($line -match '^[a-zA-Z].*:' -and $skipScripts) {
$skipScripts = $false
}
if ($skipScripts -and $line -match '^\s+') {
continue
}
}
$outputLines += $line
}
$body = $outputLines -join "`n"
# Apply other substitutions
$body = $body -replace '\{ARGS\}', $ArgFormat
$body = $body -replace '__AGENT__', $Agent
$body = Rewrite-Paths -Content $body
# Generate output file based on extension
$outputFile = Join-Path $OutputDir "speckit.$name.$Extension"
switch ($Extension) {
'toml' {
$body = $body -replace '\\', '\\'
$output = "description = `"$description`"`n`nprompt = `"`"`"`n$body`n`"`"`""
Set-Content -Path $outputFile -Value $output -NoNewline
}
'md' {
Set-Content -Path $outputFile -Value $body -NoNewline
}
'agent.md' {
Set-Content -Path $outputFile -Value $body -NoNewline
}
}
}
}
function Generate-CopilotPrompts {
param(
[string]$AgentsDir,
[string]$PromptsDir
)
New-Item -ItemType Directory -Path $PromptsDir -Force | Out-Null
$agentFiles = Get-ChildItem -Path "$AgentsDir/speckit.*.agent.md" -File -ErrorAction SilentlyContinue
foreach ($agentFile in $agentFiles) {
$basename = $agentFile.Name -replace '\.agent\.md$', ''
$promptFile = Join-Path $PromptsDir "$basename.prompt.md"
$content = @"
---
agent: $basename
---
"@
Set-Content -Path $promptFile -Value $content
}
}
function Build-Variant {
param(
[string]$Agent,
[string]$Script
)
$baseDir = Join-Path $GenReleasesDir "sdd-${Agent}-package-${Script}"
Write-Host "Building $Agent ($Script) package..."
New-Item -ItemType Directory -Path $baseDir -Force | Out-Null
# Copy base structure but filter scripts by variant
$specDir = Join-Path $baseDir ".specify"
New-Item -ItemType Directory -Path $specDir -Force | Out-Null
# Copy memory directory
if (Test-Path "memory") {
Copy-Item -Path "memory" -Destination $specDir -Recurse -Force
Write-Host "Copied memory -> .specify"
}
# Only copy the relevant script variant directory
if (Test-Path "scripts") {
$scriptsDestDir = Join-Path $specDir "scripts"
New-Item -ItemType Directory -Path $scriptsDestDir -Force | Out-Null
switch ($Script) {
'sh' {
if (Test-Path "scripts/bash") {
Copy-Item -Path "scripts/bash" -Destination $scriptsDestDir -Recurse -Force
Write-Host "Copied scripts/bash -> .specify/scripts"
}
}
'ps' {
if (Test-Path "scripts/powershell") {
Copy-Item -Path "scripts/powershell" -Destination $scriptsDestDir -Recurse -Force
Write-Host "Copied scripts/powershell -> .specify/scripts"
}
}
}
# Copy any script files that aren't in variant-specific directories
Get-ChildItem -Path "scripts" -File -ErrorAction SilentlyContinue | ForEach-Object {
Copy-Item -Path $_.FullName -Destination $scriptsDestDir -Force
}
}
# Copy templates (excluding commands directory and vscode-settings.json)
if (Test-Path "templates") {
$templatesDestDir = Join-Path $specDir "templates"
New-Item -ItemType Directory -Path $templatesDestDir -Force | Out-Null
Get-ChildItem -Path "templates" -Recurse -File | Where-Object {
$_.FullName -notmatch 'templates[/\\]commands[/\\]' -and $_.Name -ne 'vscode-settings.json'
} | ForEach-Object {
$relativePath = $_.FullName.Substring((Resolve-Path "templates").Path.Length + 1)
$destFile = Join-Path $templatesDestDir $relativePath
$destFileDir = Split-Path $destFile -Parent
New-Item -ItemType Directory -Path $destFileDir -Force | Out-Null
Copy-Item -Path $_.FullName -Destination $destFile -Force
}
Write-Host "Copied templates -> .specify/templates"
}
# Generate agent-specific command files
switch ($Agent) {
'claude' {
$cmdDir = Join-Path $baseDir ".claude/commands"
Generate-Commands -Agent 'claude' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'gemini' {
$cmdDir = Join-Path $baseDir ".gemini/commands"
Generate-Commands -Agent 'gemini' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script
if (Test-Path "agent_templates/gemini/GEMINI.md") {
Copy-Item -Path "agent_templates/gemini/GEMINI.md" -Destination (Join-Path $baseDir "GEMINI.md")
}
}
'copilot' {
$agentsDir = Join-Path $baseDir ".github/agents"
Generate-Commands -Agent 'copilot' -Extension 'agent.md' -ArgFormat '$ARGUMENTS' -OutputDir $agentsDir -ScriptVariant $Script
# Generate companion prompt files
$promptsDir = Join-Path $baseDir ".github/prompts"
Generate-CopilotPrompts -AgentsDir $agentsDir -PromptsDir $promptsDir
# Create VS Code workspace settings
$vscodeDir = Join-Path $baseDir ".vscode"
New-Item -ItemType Directory -Path $vscodeDir -Force | Out-Null
if (Test-Path "templates/vscode-settings.json") {
Copy-Item -Path "templates/vscode-settings.json" -Destination (Join-Path $vscodeDir "settings.json")
}
}
'cursor-agent' {
$cmdDir = Join-Path $baseDir ".cursor/commands"
Generate-Commands -Agent 'cursor-agent' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'qwen' {
$cmdDir = Join-Path $baseDir ".qwen/commands"
Generate-Commands -Agent 'qwen' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script
if (Test-Path "agent_templates/qwen/QWEN.md") {
Copy-Item -Path "agent_templates/qwen/QWEN.md" -Destination (Join-Path $baseDir "QWEN.md")
}
}
'opencode' {
$cmdDir = Join-Path $baseDir ".opencode/command"
Generate-Commands -Agent 'opencode' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'windsurf' {
$cmdDir = Join-Path $baseDir ".windsurf/workflows"
Generate-Commands -Agent 'windsurf' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'codex' {
$cmdDir = Join-Path $baseDir ".codex/prompts"
Generate-Commands -Agent 'codex' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'kilocode' {
$cmdDir = Join-Path $baseDir ".kilocode/workflows"
Generate-Commands -Agent 'kilocode' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'auggie' {
$cmdDir = Join-Path $baseDir ".augment/commands"
Generate-Commands -Agent 'auggie' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'roo' {
$cmdDir = Join-Path $baseDir ".roo/commands"
Generate-Commands -Agent 'roo' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'codebuddy' {
$cmdDir = Join-Path $baseDir ".codebuddy/commands"
Generate-Commands -Agent 'codebuddy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'amp' {
$cmdDir = Join-Path $baseDir ".agents/commands"
Generate-Commands -Agent 'amp' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'q' {
$cmdDir = Join-Path $baseDir ".amazonq/prompts"
Generate-Commands -Agent 'q' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'bob' {
$cmdDir = Join-Path $baseDir ".bob/commands"
Generate-Commands -Agent 'bob' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
'qoder' {
$cmdDir = Join-Path $baseDir ".qoder/commands"
Generate-Commands -Agent 'qoder' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
}
}
# Create zip archive
$zipFile = Join-Path $GenReleasesDir "spec-kit-template-${Agent}-${Script}-${Version}.zip"
Compress-Archive -Path "$baseDir/*" -DestinationPath $zipFile -Force
Write-Host "Created $zipFile"
}
# Define all agents and scripts
$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qoder')
$AllScripts = @('sh', 'ps')
function Normalize-List {
param([string]$Input)
if ([string]::IsNullOrEmpty($Input)) {
return @()
}
# Split by comma or space and remove duplicates while preserving order
$items = $Input -split '[,\s]+' | Where-Object { $_ } | Select-Object -Unique
return $items
}
function Validate-Subset {
param(
[string]$Type,
[string[]]$Allowed,
[string[]]$Items
)
$ok = $true
foreach ($item in $Items) {
if ($item -notin $Allowed) {
Write-Error "Unknown $Type '$item' (allowed: $($Allowed -join ', '))"
$ok = $false
}
}
return $ok
}
# Determine agent list
if (-not [string]::IsNullOrEmpty($Agents)) {
$AgentList = Normalize-List -Input $Agents
if (-not (Validate-Subset -Type 'agent' -Allowed $AllAgents -Items $AgentList)) {
exit 1
}
} else {
$AgentList = $AllAgents
}
# Determine script list
if (-not [string]::IsNullOrEmpty($Scripts)) {
$ScriptList = Normalize-List -Input $Scripts
if (-not (Validate-Subset -Type 'script' -Allowed $AllScripts -Items $ScriptList)) {
exit 1
}
} else {
$ScriptList = $AllScripts
}
Write-Host "Agents: $($AgentList -join ', ')"
Write-Host "Scripts: $($ScriptList -join ', ')"
# Build all variants
foreach ($agent in $AgentList) {
foreach ($script in $ScriptList) {
Build-Variant -Agent $agent -Script $script
}
}
Write-Host "`nArchives in ${GenReleasesDir}:"
Get-ChildItem -Path $GenReleasesDir -Filter "spec-kit-template-*-${Version}.zip" | ForEach-Object {
Write-Host " $($_.Name)"
}

41
.github/workflows/scripts/create-release-packages.sh vendored Executable file → Normal file
View File

@@ -6,7 +6,7 @@ set -euo pipefail
# Usage: .github/workflows/scripts/create-release-packages.sh <version> # Usage: .github/workflows/scripts/create-release-packages.sh <version>
# Version argument should include leading 'v'. # Version argument should include leading 'v'.
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built. # Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp shai bob (default: all) # AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp (default: all)
# SCRIPTS : space or comma separated subset of: sh ps (default: both) # SCRIPTS : space or comma separated subset of: sh ps (default: both)
# Examples: # Examples:
# AGENTS=claude SCRIPTS=sh $0 v0.2.0 # AGENTS=claude SCRIPTS=sh $0 v0.2.0
@@ -95,21 +95,21 @@ generate_commands() {
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;; { echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
md) md)
echo "$body" > "$output_dir/speckit.$name.$ext" ;; echo "$body" > "$output_dir/speckit.$name.$ext" ;;
agent.md) chatmode.md)
echo "$body" > "$output_dir/speckit.$name.$ext" ;; echo "$body" > "$output_dir/speckit.$name.$ext" ;;
esac esac
done done
} }
generate_copilot_prompts() { generate_copilot_prompts() {
local agents_dir=$1 prompts_dir=$2 local chatmodes_dir=$1 prompts_dir=$2
mkdir -p "$prompts_dir" mkdir -p "$prompts_dir"
# Generate a .prompt.md file for each .agent.md file # Generate a .prompt.md file for each .chatmode.md file
for agent_file in "$agents_dir"/speckit.*.agent.md; do for chatmode_file in "$chatmodes_dir"/speckit.*.chatmode.md; do
[[ -f "$agent_file" ]] || continue [[ -f "$chatmode_file" ]] || continue
local basename=$(basename "$agent_file" .agent.md) local basename=$(basename "$chatmode_file" .chatmode.md)
local prompt_file="$prompts_dir/${basename}.prompt.md" local prompt_file="$prompts_dir/${basename}.prompt.md"
# Create prompt file with agent frontmatter # Create prompt file with agent frontmatter
@@ -166,10 +166,10 @@ build_variant() {
generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script" generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script"
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;; [[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;;
copilot) copilot)
mkdir -p "$base_dir/.github/agents" mkdir -p "$base_dir/.github/chatmodes"
generate_commands copilot agent.md "\$ARGUMENTS" "$base_dir/.github/agents" "$script" generate_commands copilot chatmode.md "\$ARGUMENTS" "$base_dir/.github/chatmodes" "$script"
# Generate companion prompt files # Generate companion prompt files
generate_copilot_prompts "$base_dir/.github/agents" "$base_dir/.github/prompts" generate_copilot_prompts "$base_dir/.github/chatmodes" "$base_dir/.github/prompts"
# Create VS Code workspace settings # Create VS Code workspace settings
mkdir -p "$base_dir/.vscode" mkdir -p "$base_dir/.vscode"
[[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json" [[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json"
@@ -202,47 +202,38 @@ build_variant() {
codebuddy) codebuddy)
mkdir -p "$base_dir/.codebuddy/commands" mkdir -p "$base_dir/.codebuddy/commands"
generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;; generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;;
qoder)
mkdir -p "$base_dir/.qoder/commands"
generate_commands qoder md "\$ARGUMENTS" "$base_dir/.qoder/commands" "$script" ;;
amp) amp)
mkdir -p "$base_dir/.agents/commands" mkdir -p "$base_dir/.agents/commands"
generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;; generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;;
shai)
mkdir -p "$base_dir/.shai/commands"
generate_commands shai md "\$ARGUMENTS" "$base_dir/.shai/commands" "$script" ;;
q) q)
mkdir -p "$base_dir/.amazonq/prompts" mkdir -p "$base_dir/.amazonq/prompts"
generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;; generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;;
bob)
mkdir -p "$base_dir/.bob/commands"
generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;
esac esac
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . ) ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
} }
# Determine agent list # Determine agent list
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q bob qoder) ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp q)
ALL_SCRIPTS=(sh ps) ALL_SCRIPTS=(sh ps)
norm_list() { norm_list() {
# convert comma+space separated -> line separated unique while preserving order of first occurrence # convert comma+space separated -> space separated unique while preserving order of first occurrence
tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?"\n":"") $i);out=1}}}END{printf("\n")}' tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?" ":"") $i)}}}END{printf("\n")}'
} }
validate_subset() { validate_subset() {
local type=$1; shift; local -n allowed=$1; shift; local items=("$@") local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
local invalid=0 local ok=1
for it in "${items[@]}"; do for it in "${items[@]}"; do
local found=0 local found=0
for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
if [[ $found -eq 0 ]]; then if [[ $found -eq 0 ]]; then
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2 echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
invalid=1 ok=0
fi fi
done done
return $invalid return $ok
} }
if [[ -n ${AGENTS:-} ]]; then if [[ -n ${AGENTS:-} ]]; then

1
.gitignore vendored
View File

@@ -32,7 +32,6 @@ env/
*.swp *.swp
*.swo *.swo
.DS_Store .DS_Store
*.tmp
# Project specific # Project specific
*.log *.log

View File

@@ -33,7 +33,7 @@ Specify supports multiple AI agents by generating agent-specific command files a
|-------|-----------|---------|----------|-------------| |-------|-----------|---------|----------|-------------|
| **Claude Code** | `.claude/commands/` | Markdown | `claude` | Anthropic's Claude Code CLI | | **Claude Code** | `.claude/commands/` | Markdown | `claude` | Anthropic's Claude Code CLI |
| **Gemini CLI** | `.gemini/commands/` | TOML | `gemini` | Google's Gemini CLI | | **Gemini CLI** | `.gemini/commands/` | TOML | `gemini` | Google's Gemini CLI |
| **GitHub Copilot** | `.github/agents/` | Markdown | N/A (IDE-based) | GitHub Copilot in VS Code | | **GitHub Copilot** | `.github/chatmodes/` | Markdown | N/A (IDE-based) | GitHub Copilot in VS Code |
| **Cursor** | `.cursor/commands/` | Markdown | `cursor-agent` | Cursor CLI | | **Cursor** | `.cursor/commands/` | Markdown | `cursor-agent` | Cursor CLI |
| **Qwen Code** | `.qwen/commands/` | TOML | `qwen` | Alibaba's Qwen Code CLI | | **Qwen Code** | `.qwen/commands/` | TOML | `qwen` | Alibaba's Qwen Code CLI |
| **opencode** | `.opencode/command/` | Markdown | `opencode` | opencode CLI | | **opencode** | `.opencode/command/` | Markdown | `opencode` | opencode CLI |
@@ -43,11 +43,8 @@ Specify supports multiple AI agents by generating agent-specific command files a
| **Auggie CLI** | `.augment/rules/` | Markdown | `auggie` | Auggie CLI | | **Auggie CLI** | `.augment/rules/` | Markdown | `auggie` | Auggie CLI |
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE | | **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
| **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI | | **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI |
| **Qoder CLI** | `.qoder/commands/` | Markdown | `qoder` | Qoder CLI |
| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI | | **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI |
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI | | **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |
| **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI |
| **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE |
### Step-by-Step Integration Guide ### Step-by-Step Integration Guide
@@ -313,9 +310,7 @@ Require a command-line tool to be installed:
- **opencode**: `opencode` CLI - **opencode**: `opencode` CLI
- **Amazon Q Developer CLI**: `q` CLI - **Amazon Q Developer CLI**: `q` CLI
- **CodeBuddy CLI**: `codebuddy` CLI - **CodeBuddy CLI**: `codebuddy` CLI
- **Qoder CLI**: `qoder` CLI
- **Amp**: `amp` CLI - **Amp**: `amp` CLI
- **SHAI**: `shai` CLI
### IDE-Based Agents ### IDE-Based Agents
@@ -323,13 +318,12 @@ Work within integrated development environments:
- **GitHub Copilot**: Built into VS Code/compatible editors - **GitHub Copilot**: Built into VS Code/compatible editors
- **Windsurf**: Built into Windsurf IDE - **Windsurf**: Built into Windsurf IDE
- **IBM Bob**: Built into IBM Bob IDE
## Command File Formats ## Command File Formats
### Markdown Format ### Markdown Format
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp, SHAI, IBM Bob Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp
**Standard format:** **Standard format:**
@@ -368,7 +362,7 @@ Command content with {SCRIPT} and {{args}} placeholders.
- **CLI agents**: Usually `.<agent-name>/commands/` - **CLI agents**: Usually `.<agent-name>/commands/`
- **IDE agents**: Follow IDE-specific patterns: - **IDE agents**: Follow IDE-specific patterns:
- Copilot: `.github/agents/` - Copilot: `.github/chatmodes/`
- Cursor: `.cursor/commands/` - Cursor: `.cursor/commands/`
- Windsurf: `.windsurf/workflows/` - Windsurf: `.windsurf/workflows/`

View File

@@ -7,14 +7,6 @@ All notable changes to the Specify CLI and templates are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.0.22] - 2025-11-07
- Support for VS Code/Copilot agents, and moving away from prompts to proper agents with hand-offs.
- Move to use `AGENTS.md` for Copilot workloads, since it's already supported out-of-the-box.
- Adds support for the version command. ([#486](https://github.com/github/spec-kit/issues/486))
- Fixes potential bug with the `create-new-feature.ps1` script that ignores existing feature branches when determining next feature number ([#975](https://github.com/github/spec-kit/issues/975))
- Add graceful fallback and logging for GitHub API rate-limiting during template fetch ([#970](https://github.com/github/spec-kit/issues/970))
## [0.0.21] - 2025-10-21 ## [0.0.21] - 2025-10-21
- Fixes [#975](https://github.com/github/spec-kit/issues/975) (thank you [@fgalarraga](https://github.com/fgalarraga)). - Fixes [#975](https://github.com/github/spec-kit/issues/975) (thank you [@fgalarraga](https://github.com/fgalarraga)).

View File

@@ -1,5 +1,5 @@
<div align="center"> <div align="center">
<img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/> <img src="./media/logo_small.webp" alt="Spec Kit Logo"/>
<h1>🌱 Spec Kit</h1> <h1>🌱 Spec Kit</h1>
<h3><em>Build high-quality software faster.</em></h3> <h3><em>Build high-quality software faster.</em></h3>
</div> </div>
@@ -61,7 +61,7 @@ specify init <PROJECT_NAME>
specify check specify check
``` ```
To upgrade Specify, see the [Upgrade Guide](./docs/upgrade.md) for detailed instructions. Quick upgrade: To upgrade specify run:
```bash ```bash
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
@@ -136,24 +136,20 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
| Agent | Support | Notes | | Agent | Support | Notes |
|-----------------------------------------------------------|---------|---------------------------------------------------| |-----------------------------------------------------------|---------|---------------------------------------------------|
| [Qoder CLI](https://qoder.com/cli) | ✅ | |
| [Amazon Q Developer CLI](https://aws.amazon.com/developer/learning/q-developer-cli/) | ⚠️ | Amazon Q Developer CLI [does not support](https://github.com/aws/amazon-q-developer-cli/issues/3064) custom arguments for slash commands. |
| [Amp](https://ampcode.com/) | ✅ | |
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | ✅ | |
| [Claude Code](https://www.anthropic.com/claude-code) | ✅ | | | [Claude Code](https://www.anthropic.com/claude-code) | ✅ | |
| [CodeBuddy CLI](https://www.codebuddy.ai/cli) | ✅ | |
| [Codex CLI](https://github.com/openai/codex) | ✅ | |
| [Cursor](https://cursor.sh/) | ✅ | |
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | ✅ | |
| [GitHub Copilot](https://code.visualstudio.com/) | ✅ | | | [GitHub Copilot](https://code.visualstudio.com/) | ✅ | |
| [IBM Bob](https://www.ibm.com/products/bob) | ✅ | IDE-based agent with slash command support | | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | ✅ | |
| [Jules](https://jules.google.com/) | ✅ | | | [Cursor](https://cursor.sh/) | ✅ | |
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
| [opencode](https://opencode.ai/) | ✅ | |
| [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | | | [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | |
| [Roo Code](https://roocode.com/) | ✅ | | | [opencode](https://opencode.ai/) | ✅ | |
| [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | |
| [Windsurf](https://windsurf.com/) | ✅ | | | [Windsurf](https://windsurf.com/) | ✅ | |
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | ✅ | |
| [CodeBuddy CLI](https://www.codebuddy.ai/cli) | ✅ | |
| [Roo Code](https://roocode.com/) | ✅ | |
| [Codex CLI](https://github.com/openai/codex) | ✅ | |
| [Amazon Q Developer CLI](https://aws.amazon.com/developer/learning/q-developer-cli/) | ⚠️ | Amazon Q Developer CLI [does not support](https://github.com/aws/amazon-q-developer-cli/issues/3064) custom arguments for slash commands. |
| [Amp](https://ampcode.com/) | ✅ | |
## 🔧 Specify CLI Reference ## 🔧 Specify CLI Reference
@@ -164,14 +160,14 @@ The `specify` command supports the following options:
| Command | Description | | Command | Description |
|-------------|----------------------------------------------------------------| |-------------|----------------------------------------------------------------|
| `init` | Initialize a new Specify project from the latest template | | `init` | Initialize a new Specify project from the latest template |
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`, `shai`, `qoder`) | | `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`) |
### `specify init` Arguments & Options ### `specify init` Arguments & Options
| Argument/Option | Type | Description | | Argument/Option | Type | Description |
|------------------------|----------|------------------------------------------------------------------------------| |------------------------|----------|------------------------------------------------------------------------------|
| `<project-name>` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) | | `<project-name>` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) |
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `q`, `bob`, or `qoder` | | `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, or `q` |
| `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) | | `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) |
| `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code | | `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code |
| `--no-git` | Flag | Skip git repository initialization | | `--no-git` | Flag | Skip git repository initialization |
@@ -193,21 +189,12 @@ specify init my-project --ai claude
# Initialize with Cursor support # Initialize with Cursor support
specify init my-project --ai cursor-agent specify init my-project --ai cursor-agent
# Initialize with Qoder support
specify init my-project --ai qoder
# Initialize with Windsurf support # Initialize with Windsurf support
specify init my-project --ai windsurf specify init my-project --ai windsurf
# Initialize with Amp support # Initialize with Amp support
specify init my-project --ai amp specify init my-project --ai amp
# Initialize with SHAI support
specify init my-project --ai shai
# Initialize with IBM Bob support
specify init my-project --ai bob
# Initialize with PowerShell scripts (Windows/cross-platform) # Initialize with PowerShell scripts (Windows/cross-platform)
specify init my-project --ai copilot --script ps specify init my-project --ai copilot --script ps
@@ -373,7 +360,7 @@ specify init . --force --ai claude
specify init --here --force --ai claude specify init --here --force --ai claude
``` ```
The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, or Amazon Q Developer CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command: The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, or Amazon Q Developer CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
```bash ```bash
specify init <project_name> --ai claude --ignore-agent-tools specify init <project_name> --ai claude --ignore-agent-tools

View File

@@ -12,7 +12,6 @@ Spec-Driven Development **flips the script** on traditional software development
- [Installation Guide](installation.md) - [Installation Guide](installation.md)
- [Quick Start Guide](quickstart.md) - [Quick Start Guide](quickstart.md)
- [Upgrade Guide](upgrade.md)
- [Local Development](local-development.md) - [Local Development](local-development.md)
## Core Philosophy ## Core Philosophy

View File

@@ -2,24 +2,16 @@
This guide will help you get started with Spec-Driven Development using Spec Kit. This guide will help you get started with Spec-Driven Development using Spec Kit.
> [!NOTE] > NEW: All automation scripts now provide both Bash (`.sh`) and PowerShell (`.ps1`) variants. The `specify` CLI auto-selects based on OS unless you pass `--script sh|ps`.
> All automation scripts now provide both Bash (`.sh`) and PowerShell (`.ps1`) variants. The `specify` CLI auto-selects based on OS unless you pass `--script sh|ps`.
## The 6-Step Process ## The 4-Step Process
> [!TIP] ### 1. Install Specify
> **Context Awareness**: Spec Kit commands automatically detect the active feature based on your current Git branch (e.g., `001-feature-name`). To switch between different specifications, simply switch Git branches.
### Step 1: Install Specify Initialize your project depending on the coding agent you're using:
**In your terminal**, run the `specify` CLI command to initialize your project:
```bash ```bash
# Create a new project directory
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
# OR initialize in the current directory
uvx --from git+https://github.com/github/spec-kit.git specify init .
``` ```
Pick script type explicitly (optional): Pick script type explicitly (optional):
@@ -29,71 +21,31 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script sh # Force POSIX shell uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script sh # Force POSIX shell
``` ```
### Step 2: Define Your Constitution ### 2. Create the Spec
**In your AI Agent's chat interface**, use the `/speckit.constitution` slash command to establish the core rules and principles for your project. You should provide your project's specific principles as arguments. Use the `/speckit.specify` command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.
```markdown ```bash
/speckit.constitution This project follows a "Library-First" approach. All features must be implemented as standalone libraries first. We use TDD strictly. We prefer functional programming patterns.
```
### Step 3: Create the Spec
**In the chat**, use the `/speckit.specify` slash command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.
```markdown
/speckit.specify Build an application that can help me organize my photos in separate photo albums. Albums are grouped by date and can be re-organized by dragging and dropping on the main page. Albums are never in other nested albums. Within each album, photos are previewed in a tile-like interface. /speckit.specify Build an application that can help me organize my photos in separate photo albums. Albums are grouped by date and can be re-organized by dragging and dropping on the main page. Albums are never in other nested albums. Within each album, photos are previewed in a tile-like interface.
``` ```
### Step 4: Refine the Spec ### 3. Create a Technical Implementation Plan
**In the chat**, use the `/speckit.clarify` slash command to identify and resolve ambiguities in your specification. You can provide specific focus areas as arguments. Use the `/speckit.plan` command to provide your tech stack and architecture choices.
```bash ```bash
/speckit.clarify Focus on security and performance requirements.
```
### Step 5: Create a Technical Implementation Plan
**In the chat**, use the `/speckit.plan` slash command to provide your tech stack and architecture choices.
```markdown
/speckit.plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database. /speckit.plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database.
``` ```
### Step 6: Break Down and Implement ### 4. Break Down and Implement
**In the chat**, use the `/speckit.tasks` slash command to create an actionable task list. Use `/speckit.tasks` to create an actionable task list, then ask your agent to implement the feature.
```markdown
/speckit.tasks
```
Optionally, validate the plan with `/speckit.analyze`:
```markdown
/speckit.analyze
```
Then, use the `/speckit.implement` slash command to execute the plan.
```markdown
/speckit.implement
```
## Detailed Example: Building Taskify ## Detailed Example: Building Taskify
Here's a complete example of building a team productivity platform: Here's a complete example of building a team productivity platform:
### Step 1: Define Constitution ### Step 1: Define Requirements with `/speckit.specify`
Initialize the project's constitution to set ground rules:
```markdown
/speckit.constitution Taskify is a "Security-First" application. All user inputs must be validated. We use a microservices architecture. Code must be fully documented.
```
### Step 2: Define Requirements with `/speckit.specify`
```text ```text
Develop Taskify, a team productivity platform. It should allow users to create projects, add team members, Develop Taskify, a team productivity platform. It should allow users to create projects, add team members,
@@ -102,51 +54,58 @@ let's call it "Create Taskify," let's have multiple users but the users will be
I want five users in two different categories, one product manager and four engineers. Let's create three I want five users in two different categories, one product manager and four engineers. Let's create three
different sample projects. Let's have the standard Kanban columns for the status of each task, such as "To Do," different sample projects. Let's have the standard Kanban columns for the status of each task, such as "To Do,"
"In Progress," "In Review," and "Done." There will be no login for this application as this is just the very "In Progress," "In Review," and "Done." There will be no login for this application as this is just the very
first testing thing to ensure that our basic features are set up. first testing thing to ensure that our basic features are set up. For each task in the UI for a task card,
you should be able to change the current status of the task between the different columns in the Kanban work board.
You should be able to leave an unlimited number of comments for a particular card. You should be able to, from that task
card, assign one of the valid users. When you first launch Taskify, it's going to give you a list of the five users to pick
from. There will be no password required. When you click on a user, you go into the main view, which displays the list of
projects. When you click on a project, you open the Kanban board for that project. You're going to see the columns.
You'll be able to drag and drop cards back and forth between different columns. You will see any cards that are
assigned to you, the currently logged in user, in a different color from all the other ones, so you can quickly
see yours. You can edit any comments that you make, but you can't edit comments that other people made. You can
delete any comments that you made, but you can't delete comments anybody else made.
``` ```
### Step 3: Refine the Specification ### Step 2: Refine the Specification
Use the `/speckit.clarify` command to interactively resolve any ambiguities in your specification. You can also provide specific details you want to ensure are included. After the initial specification is created, clarify any missing requirements:
```bash ```text
/speckit.clarify I want to clarify the task card details. For each task in the UI for a task card, you should be able to change the current status of the task between the different columns in the Kanban work board. You should be able to leave an unlimited number of comments for a particular card. You should be able to, from that task card, assign one of the valid users. For each sample project or project that you create there should be a variable number of tasks between 5 and 15
tasks for each one randomly distributed into different states of completion. Make sure that there's at least
one task in each stage of completion.
``` ```
You can continue to refine the spec with more details using `/speckit.clarify`: Also validate the specification checklist:
```bash ```text
/speckit.clarify When you first launch Taskify, it's going to give you a list of the five users to pick from. There will be no password required. When you click on a user, you go into the main view, which displays the list of projects. When you click on a project, you open the Kanban board for that project. You're going to see the columns. You'll be able to drag and drop cards back and forth between different columns. You will see any cards that are assigned to you, the currently logged in user, in a different color from all the other ones, so you can quickly see yours. You can edit any comments that you make, but you can't edit comments that other people made. You can delete any comments that you made, but you can't delete comments anybody else made. Read the review and acceptance checklist, and check off each item in the checklist if the feature spec meets the criteria. Leave it empty if it does not.
``` ```
### Step 4: Validate the Spec ### Step 3: Generate Technical Plan with `/speckit.plan`
Validate the specification checklist using the `/speckit.checklist` command:
```bash
/speckit.checklist
```
### Step 5: Generate Technical Plan with `/speckit.plan`
Be specific about your tech stack and technical requirements: Be specific about your tech stack and technical requirements:
```bash ```text
/speckit.plan We are going to generate this using .NET Aspire, using Postgres as the database. The frontend should use Blazor server with drag-and-drop task boards, real-time updates. There should be a REST API created with a projects API, tasks API, and a notifications API. We are going to generate this using .NET Aspire, using Postgres as the database. The frontend should use
Blazor server with drag-and-drop task boards, real-time updates. There should be a REST API created with a projects API,
tasks API, and a notifications API.
``` ```
### Step 6: Validate and Implement ### Step 4: Validate and Implement
Have your AI agent audit the implementation plan using `/speckit.analyze`: Have your AI agent audit the implementation plan:
```bash ```text
/speckit.analyze Now I want you to go and audit the implementation plan and the implementation detail files.
Read through it with an eye on determining whether or not there is a sequence of tasks that you need
to be doing that are obvious from reading this. Because I don't know if there's enough here.
``` ```
Finally, implement the solution: Finally, implement the solution:
```bash ```text
/speckit.implement implement specs/002-create-taskify/plan.md
``` ```
## Key Principles ## Key Principles
@@ -159,6 +118,6 @@ Finally, implement the solution:
## Next Steps ## Next Steps
- Read the [complete methodology](../spec-driven.md) for in-depth guidance - Read the complete methodology for in-depth guidance
- Check out [more examples](../templates) in the repository - Check out more examples in the repository
- Explore the [source code on GitHub](https://github.com/github/spec-kit) - Explore the source code on GitHub

View File

@@ -9,11 +9,10 @@
href: installation.md href: installation.md
- name: Quick Start - name: Quick Start
href: quickstart.md href: quickstart.md
- name: Upgrade
href: upgrade.md
# Development workflows # Development workflows
- name: Development - name: Development
items: items:
- name: Local Development - name: Local Development
href: local-development.md href: local-development.md

View File

@@ -1,436 +0,0 @@
# Upgrade Guide
> You have Spec Kit installed and want to upgrade to the latest version to get new features, bug fixes, or updated slash commands. This guide covers both upgrading the CLI tool and updating your project files.
---
## Quick Reference
| What to Upgrade | Command | When to Use |
|----------------|---------|-------------|
| **CLI Tool Only** | `uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git` | Get latest CLI features without touching project files |
| **Project Files** | `specify init --here --force --ai <your-agent>` | Update slash commands, templates, and scripts in your project |
| **Both** | Run CLI upgrade, then project update | Recommended for major version updates |
---
## Part 1: Upgrade the CLI Tool
The CLI tool (`specify`) is separate from your project files. Upgrade it to get the latest features and bug fixes.
### If you installed with `uv tool install`
```bash
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
```
### If you use one-shot `uvx` commands
No upgrade needed—`uvx` always fetches the latest version. Just run your commands as normal:
```bash
uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai copilot
```
### Verify the upgrade
```bash
specify check
```
This shows installed tools and confirms the CLI is working.
---
## Part 2: Updating Project Files
When Spec Kit releases new features (like new slash commands or updated templates), you need to refresh your project's Spec Kit files.
### What gets updated?
Running `specify init --here --force` will update:
-**Slash command files** (`.claude/commands/`, `.github/prompts/`, etc.)
-**Script files** (`.specify/scripts/`)
-**Template files** (`.specify/templates/`)
-**Shared memory files** (`.specify/memory/`) - **⚠️ See warnings below**
### What stays safe?
These files are **never touched** by the upgrade—the template packages don't even contain them:
-**Your specifications** (`specs/001-my-feature/spec.md`, etc.) - **CONFIRMED SAFE**
-**Your implementation plans** (`specs/001-my-feature/plan.md`, `tasks.md`, etc.) - **CONFIRMED SAFE**
-**Your source code** - **CONFIRMED SAFE**
-**Your git history** - **CONFIRMED SAFE**
The `specs/` directory is completely excluded from template packages and will never be modified during upgrades.
### Update command
Run this inside your project directory:
```bash
specify init --here --force --ai <your-agent>
```
Replace `<your-agent>` with your AI assistant. Refer to this list of [Supported AI Agents](../README.md#-supported-ai-agents)
**Example:**
```bash
specify init --here --force --ai copilot
```
### Understanding the `--force` flag
Without `--force`, the CLI warns you and asks for confirmation:
```
Warning: Current directory is not empty (25 items)
Template files will be merged with existing content and may overwrite existing files
Proceed? [y/N]
```
With `--force`, it skips the confirmation and proceeds immediately.
**Important: Your `specs/` directory is always safe.** The `--force` flag only affects template files (commands, scripts, templates, memory). Your feature specifications, plans, and tasks in `specs/` are never included in upgrade packages and cannot be overwritten.
---
## ⚠️ Important Warnings
### 1. Constitution file will be overwritten
**Known issue:** `specify init --here --force` currently overwrites `.specify/memory/constitution.md` with the default template, erasing any customizations you made.
**Workaround:**
```bash
# 1. Back up your constitution before upgrading
cp .specify/memory/constitution.md .specify/memory/constitution-backup.md
# 2. Run the upgrade
specify init --here --force --ai copilot
# 3. Restore your customized constitution
mv .specify/memory/constitution-backup.md .specify/memory/constitution.md
```
Or use git to restore it:
```bash
# After upgrade, restore from git history
git restore .specify/memory/constitution.md
```
### 2. Custom template modifications
If you customized any templates in `.specify/templates/`, the upgrade will overwrite them. Back them up first:
```bash
# Back up custom templates
cp -r .specify/templates .specify/templates-backup
# After upgrade, merge your changes back manually
```
### 3. Duplicate slash commands (IDE-based agents)
Some IDE-based agents (like Kilo Code, Windsurf) may show **duplicate slash commands** after upgrading—both old and new versions appear.
**Solution:** Manually delete the old command files from your agent's folder.
**Example for Kilo Code:**
```bash
# Navigate to the agent's commands folder
cd .kilocode/rules/
# List files and identify duplicates
ls -la
# Delete old versions (example filenames - yours may differ)
rm speckit.specify-old.md
rm speckit.plan-v1.md
```
Restart your IDE to refresh the command list.
---
## Common Scenarios
### Scenario 1: "I just want new slash commands"
```bash
# Upgrade CLI (if using persistent install)
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
# Update project files to get new commands
specify init --here --force --ai copilot
# Restore your constitution if customized
git restore .specify/memory/constitution.md
```
### Scenario 2: "I customized templates and constitution"
```bash
# 1. Back up customizations
cp .specify/memory/constitution.md /tmp/constitution-backup.md
cp -r .specify/templates /tmp/templates-backup
# 2. Upgrade CLI
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
# 3. Update project
specify init --here --force --ai copilot
# 4. Restore customizations
mv /tmp/constitution-backup.md .specify/memory/constitution.md
# Manually merge template changes if needed
```
### Scenario 3: "I see duplicate slash commands in my IDE"
This happens with IDE-based agents (Kilo Code, Windsurf, Roo Code, etc.).
```bash
# Find the agent folder (example: .kilocode/rules/)
cd .kilocode/rules/
# List all files
ls -la
# Delete old command files
rm speckit.old-command-name.md
# Restart your IDE
```
### Scenario 4: "I'm working on a project without Git"
If you initialized your project with `--no-git`, you can still upgrade:
```bash
# Manually back up files you customized
cp .specify/memory/constitution.md /tmp/constitution-backup.md
# Run upgrade
specify init --here --force --ai copilot --no-git
# Restore customizations
mv /tmp/constitution-backup.md .specify/memory/constitution.md
```
The `--no-git` flag skips git initialization but doesn't affect file updates.
---
## Using `--no-git` Flag
The `--no-git` flag tells Spec Kit to **skip git repository initialization**. This is useful when:
- You manage version control differently (Mercurial, SVN, etc.)
- Your project is part of a larger monorepo with existing git setup
- You're experimenting and don't want version control yet
**During initial setup:**
```bash
specify init my-project --ai copilot --no-git
```
**During upgrade:**
```bash
specify init --here --force --ai copilot --no-git
```
### What `--no-git` does NOT do
❌ Does NOT prevent file updates
❌ Does NOT skip slash command installation
❌ Does NOT affect template merging
It **only** skips running `git init` and creating the initial commit.
### Working without Git
If you use `--no-git`, you'll need to manage feature directories manually:
**Set the `SPECIFY_FEATURE` environment variable** before using planning commands:
```bash
# Bash/Zsh
export SPECIFY_FEATURE="001-my-feature"
# PowerShell
$env:SPECIFY_FEATURE = "001-my-feature"
```
This tells Spec Kit which feature directory to use when creating specs, plans, and tasks.
**Why this matters:** Without git, Spec Kit can't detect your current branch name to determine the active feature. The environment variable provides that context manually.
---
## Troubleshooting
### "Slash commands not showing up after upgrade"
**Cause:** Agent didn't reload the command files.
**Fix:**
1. **Restart your IDE/editor** completely (not just reload window)
2. **For CLI-based agents**, verify files exist:
```bash
ls -la .claude/commands/ # Claude Code
ls -la .gemini/commands/ # Gemini
ls -la .cursor/commands/ # Cursor
```
3. **Check agent-specific setup:**
- Codex requires `CODEX_HOME` environment variable
- Some agents need workspace restart or cache clearing
### "I lost my constitution customizations"
**Fix:** Restore from git or backup:
```bash
# If you committed before upgrading
git restore .specify/memory/constitution.md
# If you backed up manually
cp /tmp/constitution-backup.md .specify/memory/constitution.md
```
**Prevention:** Always commit or back up `constitution.md` before upgrading.
### "Warning: Current directory is not empty"
**Full warning message:**
```
Warning: Current directory is not empty (25 items)
Template files will be merged with existing content and may overwrite existing files
Do you want to continue? [y/N]
```
**What this means:**
This warning appears when you run `specify init --here` (or `specify init .`) in a directory that already has files. It's telling you:
1. **The directory has existing content** - In the example, 25 files/folders
2. **Files will be merged** - New template files will be added alongside your existing files
3. **Some files may be overwritten** - If you already have Spec Kit files (`.claude/`, `.specify/`, etc.), they'll be replaced with the new versions
**What gets overwritten:**
Only Spec Kit infrastructure files:
- Agent command files (`.claude/commands/`, `.github/prompts/`, etc.)
- Scripts in `.specify/scripts/`
- Templates in `.specify/templates/`
- Memory files in `.specify/memory/` (including constitution)
**What stays untouched:**
- Your `specs/` directory (specifications, plans, tasks)
- Your source code files
- Your `.git/` directory and git history
- Any other files not part of Spec Kit templates
**How to respond:**
- **Type `y` and press Enter** - Proceed with the merge (recommended if upgrading)
- **Type `n` and press Enter** - Cancel the operation
- **Use `--force` flag** - Skip this confirmation entirely:
```bash
specify init --here --force --ai copilot
```
**When you see this warning:**
- ✅ **Expected** when upgrading an existing Spec Kit project
- ✅ **Expected** when adding Spec Kit to an existing codebase
- ⚠️ **Unexpected** if you thought you were creating a new project in an empty directory
**Prevention tip:** Before upgrading, commit or back up your `.specify/memory/constitution.md` if you customized it.
### "CLI upgrade doesn't seem to work"
Verify the installation:
```bash
# Check installed tools
uv tool list
# Should show specify-cli
# Verify path
which specify
# Should point to the uv tool installation directory
```
If not found, reinstall:
```bash
uv tool uninstall specify-cli
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
```
### "Do I need to run specify every time I open my project?"
**Short answer:** No, you only run `specify init` once per project (or when upgrading).
**Explanation:**
The `specify` CLI tool is used for:
- **Initial setup:** `specify init` to bootstrap Spec Kit in your project
- **Upgrades:** `specify init --here --force` to update templates and commands
- **Diagnostics:** `specify check` to verify tool installation
Once you've run `specify init`, the slash commands (like `/speckit.specify`, `/speckit.plan`, etc.) are **permanently installed** in your project's agent folder (`.claude/`, `.github/prompts/`, etc.). Your AI assistant reads these command files directly—no need to run `specify` again.
**If your agent isn't recognizing slash commands:**
1. **Verify command files exist:**
```bash
# For GitHub Copilot
ls -la .github/prompts/
# For Claude
ls -la .claude/commands/
```
2. **Restart your IDE/editor completely** (not just reload window)
3. **Check you're in the correct directory** where you ran `specify init`
4. **For some agents**, you may need to reload the workspace or clear cache
**Related issue:** If Copilot can't open local files or uses PowerShell commands unexpectedly, this is typically an IDE context issue, not related to `specify`. Try:
- Restarting VS Code
- Checking file permissions
- Ensuring the workspace folder is properly opened
---
## Version Compatibility
Spec Kit follows semantic versioning for major releases. The CLI and project files are designed to be compatible within the same major version.
**Best practice:** Keep both CLI and project files in sync by upgrading both together during major version changes.
---
## Next Steps
After upgrading:
- **Test new slash commands:** Run `/speckit.constitution` or another command to verify everything works
- **Review release notes:** Check [GitHub Releases](https://github.com/github/spec-kit/releases) for new features and breaking changes
- **Update workflows:** If new commands were added, update your team's development workflows
- **Check documentation:** Visit [github.io/spec-kit](https://github.github.io/spec-kit/) for updated guides

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "specify-cli" name = "specify-cli"
version = "0.0.22" version = "0.0.21"
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 = [

View File

@@ -75,7 +75,7 @@ EOF
done done
# Source common functions # Source common functions
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh" source "$SCRIPT_DIR/common.sh"
# Get feature paths and validate branch # Get feature paths and validate branch

View File

@@ -7,7 +7,7 @@ get_repo_root() {
git rev-parse --show-toplevel git rev-parse --show-toplevel
else else
# Fall back to script location for non-git repos # Fall back to script location for non-git repos
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "$script_dir/../../.." && pwd) (cd "$script_dir/../../.." && pwd)
fi fi
} }

View File

@@ -4,7 +4,6 @@ set -e
JSON_MODE=false JSON_MODE=false
SHORT_NAME="" SHORT_NAME=""
BRANCH_NUMBER=""
ARGS=() ARGS=()
i=1 i=1
while [ $i -le $# ]; do while [ $i -le $# ]; do
@@ -27,31 +26,17 @@ while [ $i -le $# ]; do
fi fi
SHORT_NAME="$next_arg" SHORT_NAME="$next_arg"
;; ;;
--number)
if [ $((i + 1)) -gt $# ]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
i=$((i + 1))
next_arg="${!i}"
if [[ "$next_arg" == --* ]]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
BRANCH_NUMBER="$next_arg"
;;
--help|-h) --help|-h)
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" echo "Usage: $0 [--json] [--short-name <name>] <feature_description>"
echo "" echo ""
echo "Options:" echo "Options:"
echo " --json Output in JSON format" echo " --json Output in JSON format"
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch" echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
echo " --number N Specify branch number manually (overrides auto-detection)"
echo " --help, -h Show this help message" echo " --help, -h Show this help message"
echo "" echo ""
echo "Examples:" echo "Examples:"
echo " $0 'Add user authentication system' --short-name 'user-auth'" echo " $0 'Add user authentication system' --short-name 'user-auth'"
echo " $0 'Implement OAuth2 integration for API' --number 5" echo " $0 'Implement OAuth2 integration for API'"
exit 0 exit 0
;; ;;
*) *)
@@ -63,7 +48,7 @@ done
FEATURE_DESCRIPTION="${ARGS[*]}" FEATURE_DESCRIPTION="${ARGS[*]}"
if [ -z "$FEATURE_DESCRIPTION" ]; then if [ -z "$FEATURE_DESCRIPTION" ]; then
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" >&2 echo "Usage: $0 [--json] [--short-name <name>] <feature_description>" >&2
exit 1 exit 1
fi fi
@@ -80,94 +65,10 @@ find_repo_root() {
return 1 return 1
} }
# Function to get highest number from specs directory
get_highest_from_specs() {
local specs_dir="$1"
local highest=0
if [ -d "$specs_dir" ]; then
for dir in "$specs_dir"/*; do
[ -d "$dir" ] || continue
dirname=$(basename "$dir")
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
done
fi
echo "$highest"
}
# Function to get highest number from git branches
get_highest_from_branches() {
local highest=0
# Get all branches (local and remote)
branches=$(git branch -a 2>/dev/null || echo "")
if [ -n "$branches" ]; then
while IFS= read -r branch; do
# Clean branch name: remove leading markers and remote prefixes
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
# Extract feature number if branch matches pattern ###-*
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
fi
done <<< "$branches"
fi
echo "$highest"
}
# Function to check existing branches (local and remote) and return next available number
check_existing_branches() {
local short_name="$1"
local specs_dir="$2"
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
git fetch --all --prune 2>/dev/null || true
# Find all branches matching the pattern using git ls-remote (more reliable)
local remote_branches=$(git ls-remote --heads origin 2>/dev/null | grep -E "refs/heads/[0-9]+-${short_name}$" | sed 's/.*\/\([0-9]*\)-.*/\1/' | sort -n)
# Also check local branches
local local_branches=$(git branch 2>/dev/null | grep -E "^[* ]*[0-9]+-${short_name}$" | sed 's/^[* ]*//' | sed 's/-.*//' | sort -n)
# Check specs directory as well
local spec_dirs=""
if [ -d "$specs_dir" ]; then
spec_dirs=$(find "$specs_dir" -maxdepth 1 -type d -name "[0-9]*-${short_name}" 2>/dev/null | xargs -n1 basename 2>/dev/null | sed 's/-.*//' | sort -n)
fi
# Combine all sources and get the highest number
local max_num=0
for num in $remote_branches $local_branches $spec_dirs; do
if [ "$num" -gt "$max_num" ]; then
max_num=$num
fi
done
# Return next number
echo $((max_num + 1))
}
# Function to clean and format a branch name
clean_branch_name() {
local name="$1"
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed '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.
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if git rev-parse --show-toplevel >/dev/null 2>&1; then if git rev-parse --show-toplevel >/dev/null 2>&1; then
REPO_ROOT=$(git rev-parse --show-toplevel) REPO_ROOT=$(git rev-parse --show-toplevel)
@@ -186,6 +87,48 @@ cd "$REPO_ROOT"
SPECS_DIR="$REPO_ROOT/specs" SPECS_DIR="$REPO_ROOT/specs"
mkdir -p "$SPECS_DIR" mkdir -p "$SPECS_DIR"
# Get highest number from specs directory
HIGHEST_FROM_SPECS=0
if [ -d "$SPECS_DIR" ]; then
for dir in "$SPECS_DIR"/*; do
[ -d "$dir" ] || continue
dirname=$(basename "$dir")
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$HIGHEST_FROM_SPECS" ]; then HIGHEST_FROM_SPECS=$number; fi
done
fi
# Get highest number from branch names (both local and remote)
HIGHEST_FROM_BRANCHES=0
if [ "$HAS_GIT" = true ]; then
# Get all branches (local and remote)
branches=$(git branch -a 2>/dev/null || echo "")
if [ -n "$branches" ]; then
while IFS= read -r branch; do
# Clean branch name: remove leading markers and remote prefixes
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
# Extract feature number if branch matches pattern ###-*
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$HIGHEST_FROM_BRANCHES" ]; then HIGHEST_FROM_BRANCHES=$number; fi
fi
done <<< "$branches"
fi
fi
# Use the highest number from either source
HIGHEST=$HIGHEST_FROM_SPECS
if [ "$HIGHEST_FROM_BRANCHES" -gt "$HIGHEST" ]; then
HIGHEST=$HIGHEST_FROM_BRANCHES
fi
NEXT=$((HIGHEST + 1))
FEATURE_NUM=$(printf "%03d" "$NEXT")
# Function to generate branch name with stop word filtering and length filtering # Function to generate branch name with stop word filtering and length filtering
generate_branch_name() { generate_branch_name() {
local description="$1" local description="$1"
@@ -229,33 +172,19 @@ generate_branch_name() {
echo "$result" echo "$result"
else else
# Fallback to original logic if no meaningful words found # Fallback to original logic if no meaningful words found
local cleaned=$(clean_branch_name "$description") echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//' | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
fi fi
} }
# Generate branch name # Generate branch name
if [ -n "$SHORT_NAME" ]; then if [ -n "$SHORT_NAME" ]; then
# Use provided short name, just clean it up # Use provided short name, just clean it up
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME") BRANCH_SUFFIX=$(echo "$SHORT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
else else
# Generate from description with smart filtering # Generate from description with smart filtering
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION") BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
fi fi
# Determine branch number
if [ -z "$BRANCH_NUMBER" ]; then
if [ "$HAS_GIT" = true ]; then
# Check existing branches on remotes
BRANCH_NUMBER=$(check_existing_branches "$BRANCH_SUFFIX" "$SPECS_DIR")
else
# Fall back to local directory check
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
BRANCH_NUMBER=$((HIGHEST + 1))
fi
fi
FEATURE_NUM=$(printf "%03d" "$BRANCH_NUMBER")
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}" BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
# GitHub enforces a 244-byte limit on branch names # GitHub enforces a 244-byte limit on branch names

View File

@@ -24,7 +24,7 @@ for arg in "$@"; do
done done
# Get script directory and load common functions # Get script directory and load common functions
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh" source "$SCRIPT_DIR/common.sh"
# Get all paths and variables from common functions # Get all paths and variables from common functions

View File

@@ -30,12 +30,12 @@
# #
# 5. Multi-Agent Support # 5. Multi-Agent Support
# - Handles agent-specific file paths and naming conventions # - Handles agent-specific file paths and naming conventions
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, or Amazon Q Developer CLI # - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Amp, or Amazon Q Developer CLI
# - Can update single agents or all existing agent files # - Can update single agents or all existing agent files
# - Creates default Claude file if no agent files exist # - Creates default Claude file if no agent files exist
# #
# Usage: ./update-agent-context.sh [agent_type] # Usage: ./update-agent-context.sh [agent_type]
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|shai|q|bob|qoder # Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|q
# Leave empty to update all existing agent files # Leave empty to update all existing agent files
set -e set -e
@@ -49,7 +49,7 @@ set -o pipefail
#============================================================================== #==============================================================================
# Get script directory and load common functions # Get script directory and load common functions
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh" source "$SCRIPT_DIR/common.sh"
# Get all paths and variables from common functions # Get all paths and variables from common functions
@@ -61,7 +61,7 @@ AGENT_TYPE="${1:-}"
# Agent-specific file paths # Agent-specific file paths
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md" CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
GEMINI_FILE="$REPO_ROOT/GEMINI.md" GEMINI_FILE="$REPO_ROOT/GEMINI.md"
COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md" COPILOT_FILE="$REPO_ROOT/.github/chatmodes/copilot-instructions.md"
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc" CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
QWEN_FILE="$REPO_ROOT/QWEN.md" QWEN_FILE="$REPO_ROOT/QWEN.md"
AGENTS_FILE="$REPO_ROOT/AGENTS.md" AGENTS_FILE="$REPO_ROOT/AGENTS.md"
@@ -70,11 +70,8 @@ KILOCODE_FILE="$REPO_ROOT/.kilocode/rules/specify-rules.md"
AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md" AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md" ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md" CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
QODER_FILE="$REPO_ROOT/QODER.md"
AMP_FILE="$REPO_ROOT/AGENTS.md" AMP_FILE="$REPO_ROOT/AGENTS.md"
SHAI_FILE="$REPO_ROOT/SHAI.md"
Q_FILE="$REPO_ROOT/AGENTS.md" Q_FILE="$REPO_ROOT/AGENTS.md"
BOB_FILE="$REPO_ROOT/AGENTS.md"
# Template file # Template file
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md" TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
@@ -618,24 +615,15 @@ update_specific_agent() {
codebuddy) codebuddy)
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI" update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
;; ;;
qoder)
update_agent_file "$QODER_FILE" "Qoder CLI"
;;
amp) amp)
update_agent_file "$AMP_FILE" "Amp" update_agent_file "$AMP_FILE" "Amp"
;; ;;
shai)
update_agent_file "$SHAI_FILE" "SHAI"
;;
q) q)
update_agent_file "$Q_FILE" "Amazon Q Developer CLI" update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
;; ;;
bob)
update_agent_file "$BOB_FILE" "IBM Bob"
;;
*) *)
log_error "Unknown agent type '$agent_type'" log_error "Unknown agent type '$agent_type'"
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|shai|q|bob|qoder" log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|q"
exit 1 exit 1
;; ;;
esac esac
@@ -700,26 +688,11 @@ update_all_existing_agents() {
found_agent=true found_agent=true
fi fi
if [[ -f "$SHAI_FILE" ]]; then
update_agent_file "$SHAI_FILE" "SHAI"
found_agent=true
fi
if [[ -f "$QODER_FILE" ]]; then
update_agent_file "$QODER_FILE" "Qoder CLI"
found_agent=true
fi
if [[ -f "$Q_FILE" ]]; then if [[ -f "$Q_FILE" ]]; then
update_agent_file "$Q_FILE" "Amazon Q Developer CLI" update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
found_agent=true found_agent=true
fi fi
if [[ -f "$BOB_FILE" ]]; then
update_agent_file "$BOB_FILE" "IBM Bob"
found_agent=true
fi
# 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..."
@@ -744,7 +717,7 @@ print_summary() {
echo echo
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|shai|q|bob|qoder]" log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|q]"
} }
#============================================================================== #==============================================================================

View File

@@ -4,7 +4,6 @@
param( param(
[switch]$Json, [switch]$Json,
[string]$ShortName, [string]$ShortName,
[int]$Number = 0,
[switch]$Help, [switch]$Help,
[Parameter(ValueFromRemainingArguments = $true)] [Parameter(ValueFromRemainingArguments = $true)]
[string[]]$FeatureDescription [string[]]$FeatureDescription
@@ -13,12 +12,11 @@ $ErrorActionPreference = 'Stop'
# Show help if requested # Show help if requested
if ($Help) { if ($Help) {
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] [-Number N] <feature description>" Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
Write-Host "" Write-Host ""
Write-Host "Options:" Write-Host "Options:"
Write-Host " -Json Output in JSON format" Write-Host " -Json Output in JSON format"
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch" Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
Write-Host " -Number N Specify branch number manually (overrides auto-detection)"
Write-Host " -Help Show this help message" Write-Host " -Help Show this help message"
Write-Host "" Write-Host ""
Write-Host "Examples:" Write-Host "Examples:"
@@ -58,121 +56,6 @@ function Find-RepositoryRoot {
$current = $parent $current = $parent
} }
} }
function Get-HighestNumberFromSpecs {
param([string]$SpecsDir)
$highest = 0
if (Test-Path $SpecsDir) {
Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d+)') {
$num = [int]$matches[1]
if ($num -gt $highest) { $highest = $num }
}
}
}
return $highest
}
function Get-HighestNumberFromBranches {
param()
$highest = 0
try {
$branches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0) {
foreach ($branch in $branches) {
# Clean branch name: remove leading markers and remote prefixes
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
# Extract feature number if branch matches pattern ###-*
if ($cleanBranch -match '^(\d+)-') {
$num = [int]$matches[1]
if ($num -gt $highest) { $highest = $num }
}
}
}
} catch {
# If git command fails, return 0
Write-Verbose "Could not check Git branches: $_"
}
return $highest
}
function Get-NextBranchNumber {
param(
[string]$ShortName,
[string]$SpecsDir
)
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
try {
git fetch --all --prune 2>$null | Out-Null
} catch {
# Ignore fetch errors
}
# Find remote branches matching the pattern using git ls-remote
$remoteBranches = @()
try {
$remoteRefs = git ls-remote --heads origin 2>$null
if ($remoteRefs) {
$remoteBranches = $remoteRefs | Where-Object { $_ -match "refs/heads/(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_ -match "refs/heads/(\d+)-") {
[int]$matches[1]
}
}
}
} catch {
# Ignore errors
}
# Check local branches
$localBranches = @()
try {
$allBranches = git branch 2>$null
if ($allBranches) {
$localBranches = $allBranches | Where-Object { $_ -match "^\*?\s*(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_ -match "(\d+)-") {
[int]$matches[1]
}
}
}
} catch {
# Ignore errors
}
# Check specs directory
$specDirs = @()
if (Test-Path $SpecsDir) {
try {
$specDirs = Get-ChildItem -Path $SpecsDir -Directory | Where-Object { $_.Name -match "^(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object {
if ($_.Name -match "^(\d+)-") {
[int]$matches[1]
}
}
} catch {
# Ignore errors
}
}
# Combine all sources and get the highest number
$maxNum = 0
foreach ($num in ($remoteBranches + $localBranches + $specDirs)) {
if ($num -gt $maxNum) {
$maxNum = $num
}
}
# Return next number
return $maxNum + 1
}
function ConvertTo-CleanBranchName {
param([string]$Name)
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
}
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot) $fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
if (-not $fallbackRoot) { if (-not $fallbackRoot) {
Write-Error "Error: Could not determine repository root. Please run this script from within the repository." Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
@@ -196,6 +79,43 @@ Set-Location $repoRoot
$specsDir = Join-Path $repoRoot 'specs' $specsDir = Join-Path $repoRoot 'specs'
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
# Get highest number from specs directory
$highestFromSpecs = 0
if (Test-Path $specsDir) {
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d{3})') {
$num = [int]$matches[1]
if ($num -gt $highestFromSpecs) { $highestFromSpecs = $num }
}
}
}
# Get highest number from branch names (both local and remote)
$highestFromBranches = 0
try {
$branches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0) {
foreach ($branch in $branches) {
# Clean branch name: remove leading markers and remote prefixes
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
# Extract feature number if branch matches pattern ###-*
if ($cleanBranch -match '^(\d{3})-') {
$num = [int]$matches[1]
if ($num -gt $highestFromBranches) { $highestFromBranches = $num }
}
}
}
} catch {
# If git command fails, just continue with specs-only check
Write-Verbose "Could not check Git branches: $_"
}
# Use the highest number from either source
$highest = [Math]::Max($highestFromSpecs, $highestFromBranches)
$next = $highest + 1
$featureNum = ('{0:000}' -f $next)
# Function to generate branch name with stop word filtering and length filtering # Function to generate branch name with stop word filtering and length filtering
function Get-BranchName { function Get-BranchName {
param([string]$Description) param([string]$Description)
@@ -235,7 +155,7 @@ function Get-BranchName {
return $result return $result
} else { } else {
# Fallback to original logic if no meaningful words found # Fallback to original logic if no meaningful words found
$result = ConvertTo-CleanBranchName -Name $Description $result = $Description.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3 $fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
return [string]::Join('-', $fallbackWords) return [string]::Join('-', $fallbackWords)
} }
@@ -244,24 +164,12 @@ function Get-BranchName {
# Generate branch name # Generate branch name
if ($ShortName) { if ($ShortName) {
# Use provided short name, just clean it up # Use provided short name, just clean it up
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName $branchSuffix = $ShortName.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
} else { } else {
# Generate from description with smart filtering # Generate from description with smart filtering
$branchSuffix = Get-BranchName -Description $featureDesc $branchSuffix = Get-BranchName -Description $featureDesc
} }
# Determine branch number
if ($Number -eq 0) {
if ($hasGit) {
# Check existing branches on remotes
$Number = Get-NextBranchNumber -ShortName $branchSuffix -SpecsDir $specsDir
} else {
# Fall back to local directory check
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
}
}
$featureNum = ('{0:000}' -f $Number)
$branchName = "$featureNum-$branchSuffix" $branchName = "$featureNum-$branchSuffix"
# GitHub enforces a 244-byte limit on branch names # GitHub enforces a 244-byte limit on branch names

View File

@@ -59,3 +59,4 @@ if ($Json) {
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)" Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
Write-Output "HAS_GIT: $($paths.HAS_GIT)" Write-Output "HAS_GIT: $($paths.HAS_GIT)"
} }

View File

@@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh:
2. Plan Data Extraction 2. Plan Data Extraction
3. Agent File Management (create from template or update existing) 3. Agent File Management (create from template or update existing)
4. Content Generation (technology stack, recent changes, timestamp) 4. Content Generation (technology stack, recent changes, timestamp)
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, q, bob, qoder) 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, amp, q)
.PARAMETER AgentType .PARAMETER AgentType
Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist). Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
@@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1
#> #>
param( param(
[Parameter(Position=0)] [Parameter(Position=0)]
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','q','bob','qoder')] [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','q')]
[string]$AgentType [string]$AgentType
) )
@@ -46,7 +46,7 @@ $NEW_PLAN = $IMPL_PLAN
# Agent file paths # Agent file paths
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md' $CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md' $GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
$COPILOT_FILE = Join-Path $REPO_ROOT '.github/agents/copilot-instructions.md' $COPILOT_FILE = Join-Path $REPO_ROOT '.github/chatmodes/copilot-instructions.md'
$CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc' $CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc'
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md' $QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
@@ -55,11 +55,8 @@ $KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/rules/specify-rules.md'
$AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md' $AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md'
$ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md' $ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md' $CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
$QODER_FILE = Join-Path $REPO_ROOT 'QODER.md'
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md'
$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md' $TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
@@ -383,12 +380,9 @@ function Update-SpecificAgent {
'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' } 'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' } 'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' } 'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
'qoder' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' }
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' } 'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' }
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' } 'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' } default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q'; return $false }
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|bob|qoder'; return $false }
} }
} }
@@ -406,10 +400,7 @@ function Update-AllExistingAgents {
if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true } if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true }
if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true } if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true }
if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true } if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true }
if (Test-Path $QODER_FILE) { if (-not (Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI')) { $ok = $false }; $found = $true }
if (Test-Path $SHAI_FILE) { if (-not (Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI')) { $ok = $false }; $found = $true }
if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true } if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true }
if (Test-Path $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true }
if (-not $found) { if (-not $found) {
Write-Info 'No existing agent files found, creating default Claude file...' Write-Info 'No existing agent files found, creating default Claude file...'
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false } if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
@@ -424,7 +415,7 @@ function Print-Summary {
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" } if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" } if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
Write-Host '' Write-Host ''
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|bob|qoder]' Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q]'
} }
function Main { function Main {

View File

@@ -190,12 +190,6 @@ AGENT_CONFIG = {
"install_url": "https://www.codebuddy.ai/cli", "install_url": "https://www.codebuddy.ai/cli",
"requires_cli": True, "requires_cli": True,
}, },
"qoder": {
"name": "Qoder CLI",
"folder": ".qoder/",
"install_url": "https://qoder.com/cli",
"requires_cli": True,
},
"roo": { "roo": {
"name": "Roo Code", "name": "Roo Code",
"folder": ".roo/", "folder": ".roo/",
@@ -214,18 +208,6 @@ AGENT_CONFIG = {
"install_url": "https://ampcode.com/manual#install", "install_url": "https://ampcode.com/manual#install",
"requires_cli": True, "requires_cli": True,
}, },
"shai": {
"name": "SHAI",
"folder": ".shai/",
"install_url": "https://github.com/ovh/shai",
"requires_cli": True,
},
"bob": {
"name": "IBM Bob",
"folder": ".bob/",
"install_url": None, # IDE-based
"requires_cli": False,
},
} }
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
@@ -945,7 +927,7 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None =
@app.command() @app.command()
def init( def init(
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here, or use '.' for current directory)"), project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here, or use '.' for current directory)"),
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, shai, q, bob, or qoder "), ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, or q"),
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"), script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"),
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"), ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"),
no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"), no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
@@ -1346,7 +1328,7 @@ def version():
info_table.add_row("Template Version", template_version) info_table.add_row("Template Version", template_version)
info_table.add_row("Released", release_date) info_table.add_row("Released", release_date)
info_table.add_row("", "") info_table.add_row("", "")
info_table.add_row("Python", platform.python_version()) info_table.add_row("Python", f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
info_table.add_row("Platform", platform.system()) info_table.add_row("Platform", platform.system())
info_table.add_row("Architecture", platform.machine()) info_table.add_row("Architecture", platform.machine())
info_table.add_row("OS Version", platform.version()) info_table.add_row("OS Version", platform.version())

View File

@@ -67,8 +67,7 @@ You **MUST** consider the user input before proceeding (if not empty).
``` ```
- Check if Dockerfile* exists or Docker in plan.md → create/verify .dockerignore - Check if Dockerfile* exists or Docker in plan.md → create/verify .dockerignore
- Check if .eslintrc* exists → create/verify .eslintignore - Check if .eslintrc*or eslint.config.* exists → create/verify .eslintignore
- Check if eslint.config.* exists → ensure the config's `ignores` entries cover required patterns
- Check if .prettierrc* exists → create/verify .prettierignore - Check if .prettierrc* exists → create/verify .prettierignore
- Check if .npmrc or package.json exists → create/verify .npmignore (if publishing) - Check if .npmrc or package.json exists → create/verify .npmignore (if publishing)
- Check if terraform files (*.tf) exist → create/verify .terraformignore - Check if terraform files (*.tf) exist → create/verify .terraformignore

View File

@@ -39,36 +39,16 @@ Given that feature description, do this:
- "Create a dashboard for analytics" → "analytics-dashboard" - "Create a dashboard for analytics" → "analytics-dashboard"
- "Fix payment processing timeout bug" → "fix-payment-timeout" - "Fix payment processing timeout bug" → "fix-payment-timeout"
2. **Check for existing branches before creating new one**: 2. Run the script `{SCRIPT}` from repo root **with the short-name argument** and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
a. First, fetch all remote branches to ensure we have the latest information:
```bash
git fetch --all --prune
```
b. Find the highest feature number across all sources for the short-name:
- Remote branches: `git ls-remote --heads origin | grep -E 'refs/heads/[0-9]+-<short-name>$'`
- Local branches: `git branch | grep -E '^[* ]*[0-9]+-<short-name>$'`
- Specs directories: Check for directories matching `specs/[0-9]+-<short-name>`
c. Determine the next available number:
- Extract all numbers from all three sources
- Find the highest number N
- Use N+1 for the new branch number
d. Run the script `{SCRIPT}` with the calculated number and short-name:
- Pass `--number N+1` and `--short-name "your-short-name"` along with the feature description
- Bash example: `{SCRIPT} --json --number 5 --short-name "user-auth" "Add user authentication"`
- PowerShell example: `{SCRIPT} -Json -Number 5 -ShortName "user-auth" "Add user authentication"`
**IMPORTANT**: **IMPORTANT**:
- Check all three sources (remote branches, local branches, specs directories) to find the highest number
- Only match branches/directories with the exact short-name pattern - Append the short-name argument to the `{SCRIPT}` command with the 2-4 word short name you created in step 1. Keep the feature description as the final argument.
- If no existing branches/directories found with this short-name, start with number 1 - Bash example: `--short-name "your-generated-short-name" "Feature description here"`
- You must only ever run this script once per feature - PowerShell example: `-ShortName "your-generated-short-name" "Feature description here"`
- The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for
- The JSON output will contain BRANCH_NAME and SPEC_FILE paths
- For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot") - For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot")
- You must only ever run this script once
- The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for
3. Load `templates/spec-template.md` to understand required sections. 3. Load `templates/spec-template.md` to understand required sections.

View File

@@ -1,6 +1,6 @@
--- ---
description: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts. description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
tools: ['github/github-mcp-server/issue_write'] tools: ['github/github-mcp-server/create_issue']
scripts: scripts:
sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks
ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks