mirror of
https://github.com/github/spec-kit.git
synced 2026-02-03 14:33:36 +00:00
Compare commits
39 Commits
v0.0.74
...
copilot/st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52394c6447 | ||
|
|
dafab39483 | ||
|
|
09274437fc | ||
|
|
5f1fc6b445 | ||
|
|
779e1f8afd | ||
|
|
177dcadd8c | ||
|
|
ba861cd165 | ||
|
|
926836e0fc | ||
|
|
af88930ffc | ||
|
|
89f5f9c0b9 | ||
|
|
9809b1a4ab | ||
|
|
7b536b578d | ||
|
|
7522eb3f9d | ||
|
|
d550634d8e | ||
|
|
72cb885eb7 | ||
|
|
a877af5575 | ||
|
|
2508d926c0 | ||
|
|
9f123e013a | ||
|
|
60bd9dc849 | ||
|
|
e77d99abd2 | ||
|
|
eb030dab19 | ||
|
|
be06a23fd7 | ||
|
|
d4d3139d5f | ||
|
|
65f8787b48 | ||
|
|
9786e588b7 | ||
|
|
0ac76c8c7e | ||
|
|
115b4335d9 | ||
|
|
37e87c78a0 | ||
|
|
14a574a6a8 | ||
|
|
dbd1437aea | ||
|
|
317ae4dad9 | ||
|
|
8e9d25e9be | ||
|
|
c59be99dc4 | ||
|
|
15a5630047 | ||
|
|
8de5db7a3e | ||
|
|
a97374ded0 | ||
|
|
6b58824a39 | ||
|
|
b291a6efb0 | ||
|
|
09cf4f6cc4 |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,4 +1,6 @@
|
||||
name: Lint
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -40,6 +40,8 @@ gh release create "$VERSION" \
|
||||
.genreleases/spec-kit-template-roo-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-amp-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \
|
||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||
|
||||
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
||||
# Version argument should include leading 'v'.
|
||||
# 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 (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)
|
||||
# Examples:
|
||||
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
|
||||
@@ -95,12 +95,32 @@ generate_commands() {
|
||||
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
|
||||
md)
|
||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||
prompt.md)
|
||||
chatmode.md)
|
||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
generate_copilot_prompts() {
|
||||
local chatmodes_dir=$1 prompts_dir=$2
|
||||
mkdir -p "$prompts_dir"
|
||||
|
||||
# Generate a .prompt.md file for each .chatmode.md file
|
||||
for chatmode_file in "$chatmodes_dir"/speckit.*.chatmode.md; do
|
||||
[[ -f "$chatmode_file" ]] || continue
|
||||
|
||||
local basename=$(basename "$chatmode_file" .chatmode.md)
|
||||
local prompt_file="$prompts_dir/${basename}.prompt.md"
|
||||
|
||||
# Create prompt file with agent frontmatter
|
||||
cat > "$prompt_file" <<EOF
|
||||
---
|
||||
agent: ${basename}
|
||||
---
|
||||
EOF
|
||||
done
|
||||
}
|
||||
|
||||
build_variant() {
|
||||
local agent=$1 script=$2
|
||||
local base_dir="$GENRELEASES_DIR/sdd-${agent}-package-${script}"
|
||||
@@ -146,8 +166,10 @@ build_variant() {
|
||||
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" ;;
|
||||
copilot)
|
||||
mkdir -p "$base_dir/.github/prompts"
|
||||
generate_commands copilot prompt.md "\$ARGUMENTS" "$base_dir/.github/prompts" "$script"
|
||||
mkdir -p "$base_dir/.github/chatmodes"
|
||||
generate_commands copilot chatmode.md "\$ARGUMENTS" "$base_dir/.github/chatmodes" "$script"
|
||||
# Generate companion prompt files
|
||||
generate_copilot_prompts "$base_dir/.github/chatmodes" "$base_dir/.github/prompts"
|
||||
# Create VS Code workspace settings
|
||||
mkdir -p "$base_dir/.vscode"
|
||||
[[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json"
|
||||
@@ -180,7 +202,9 @@ build_variant() {
|
||||
codebuddy)
|
||||
mkdir -p "$base_dir/.codebuddy/commands"
|
||||
generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;;
|
||||
|
||||
amp)
|
||||
mkdir -p "$base_dir/.agents/commands"
|
||||
generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;;
|
||||
q)
|
||||
mkdir -p "$base_dir/.amazonq/prompts"
|
||||
generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;;
|
||||
@@ -190,7 +214,7 @@ build_variant() {
|
||||
}
|
||||
|
||||
# Determine agent list
|
||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy q)
|
||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp q)
|
||||
ALL_SCRIPTS=(sh ps)
|
||||
|
||||
norm_list() {
|
||||
|
||||
@@ -30,6 +30,10 @@ fi
|
||||
cat > release_notes.md << EOF
|
||||
This is the latest set of releases that you can use with your agent of choice. We recommend using the Specify CLI to scaffold your projects, however you can download these independently and manage them yourself.
|
||||
|
||||
## Changelog
|
||||
|
||||
$COMMITS
|
||||
|
||||
EOF
|
||||
|
||||
echo "Generated release notes:"
|
||||
|
||||
@@ -20,5 +20,8 @@
|
||||
"MD050": {
|
||||
"style": "asterisk"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ignores": [
|
||||
".genreleases/"
|
||||
]
|
||||
}
|
||||
21
AGENTS.md
21
AGENTS.md
@@ -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 |
|
||||
| **Gemini CLI** | `.gemini/commands/` | TOML | `gemini` | Google's Gemini CLI |
|
||||
| **GitHub Copilot** | `.github/prompts/` | 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 |
|
||||
| **Qwen Code** | `.qwen/commands/` | TOML | `qwen` | Alibaba's Qwen Code CLI |
|
||||
| **opencode** | `.opencode/command/` | Markdown | `opencode` | opencode CLI |
|
||||
@@ -44,6 +44,7 @@ Specify supports multiple AI agents by generating agent-specific command files a
|
||||
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
|
||||
| **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI |
|
||||
| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI |
|
||||
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |
|
||||
|
||||
### Step-by-Step Integration Guide
|
||||
|
||||
@@ -309,6 +310,7 @@ Require a command-line tool to be installed:
|
||||
- **opencode**: `opencode` CLI
|
||||
- **Amazon Q Developer CLI**: `q` CLI
|
||||
- **CodeBuddy CLI**: `codebuddy` CLI
|
||||
- **Amp**: `amp` CLI
|
||||
|
||||
### IDE-Based Agents
|
||||
|
||||
@@ -321,7 +323,9 @@ Work within integrated development environments:
|
||||
|
||||
### Markdown Format
|
||||
|
||||
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer
|
||||
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp
|
||||
|
||||
**Standard format:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
@@ -331,6 +335,17 @@ description: "Command description"
|
||||
Command content with {SCRIPT} and $ARGUMENTS placeholders.
|
||||
```
|
||||
|
||||
**GitHub Copilot Chat Mode format:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: "Command description"
|
||||
mode: speckit.command-name
|
||||
---
|
||||
|
||||
Command content with {SCRIPT} and $ARGUMENTS placeholders.
|
||||
```
|
||||
|
||||
### TOML Format
|
||||
|
||||
Used by: Gemini, Qwen
|
||||
@@ -347,7 +362,7 @@ Command content with {SCRIPT} and {{args}} placeholders.
|
||||
|
||||
- **CLI agents**: Usually `.<agent-name>/commands/`
|
||||
- **IDE agents**: Follow IDE-specific patterns:
|
||||
- Copilot: `.github/prompts/`
|
||||
- Copilot: `.github/chatmodes/`
|
||||
- Cursor: `.cursor/commands/`
|
||||
- Windsurf: `.windsurf/workflows/`
|
||||
|
||||
|
||||
@@ -7,6 +7,14 @@ 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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.0.21] - 2025-10-21
|
||||
|
||||
- Fixes [#975](https://github.com/github/spec-kit/issues/975) (thank you [@fgalarraga](https://github.com/fgalarraga)).
|
||||
- Adds support for Amp CLI.
|
||||
- Adds support for VS Code hand-offs and moves prompts to be full-fledged chat modes.
|
||||
- Adds support for `version` command (addresses [#811](https://github.com/github/spec-kit/issues/811) and [#486](https://github.com/github/spec-kit/issues/486), thank you [@mcasalaina](https://github.com/mcasalaina) and [@dentity007](https://github.com/dentity007)).
|
||||
- Adds support for rendering the rate limit errors from the CLI when encountered ([#970](https://github.com/github/spec-kit/issues/970), thank you [@psmman](https://github.com/psmman)).
|
||||
|
||||
## [0.0.20] - 2025-10-14
|
||||
|
||||
### Added
|
||||
|
||||
@@ -62,6 +62,29 @@ When working on spec-kit:
|
||||
3. Test script functionality in the `scripts/` directory
|
||||
4. Ensure memory files (`memory/constitution.md`) are updated if major process changes are made
|
||||
|
||||
### Testing template and command changes locally
|
||||
|
||||
Running `uv run specify init` pulls released packages, which won’t include your local changes.
|
||||
To test your templates, commands, and other changes locally, follow these steps:
|
||||
|
||||
1. **Create release packages**
|
||||
|
||||
Run the following command to generate the local packages:
|
||||
|
||||
```
|
||||
./.github/workflows/scripts/create-release-packages.sh v1.0.0
|
||||
```
|
||||
|
||||
2. **Copy the relevant package to your test project**
|
||||
|
||||
```
|
||||
cp -r .genreleases/sdd-copilot-package-sh/. <path-to-test-project>/
|
||||
```
|
||||
|
||||
3. **Open and test the agent**
|
||||
|
||||
Navigate to your test project folder and open the agent to verify your implementation.
|
||||
|
||||
## AI contributions in Spec Kit
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
||||
@@ -149,6 +149,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
|
||||
| [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
|
||||
|
||||
@@ -166,7 +167,7 @@ The `specify` command supports the following options:
|
||||
| Argument/Option | Type | Description |
|
||||
|------------------------|----------|------------------------------------------------------------------------------|
|
||||
| `<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`, or `q` |
|
||||
| `--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) |
|
||||
| `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code |
|
||||
| `--no-git` | Flag | Skip git repository initialization |
|
||||
@@ -191,6 +192,9 @@ specify init my-project --ai cursor-agent
|
||||
# Initialize with Windsurf support
|
||||
specify init my-project --ai windsurf
|
||||
|
||||
# Initialize with Amp support
|
||||
specify init my-project --ai amp
|
||||
|
||||
# Initialize with PowerShell scripts (Windows/cross-platform)
|
||||
specify init my-project --ai copilot --script ps
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "specify-cli"
|
||||
version = "0.0.20"
|
||||
version = "0.0.21"
|
||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
@@ -87,17 +87,45 @@ cd "$REPO_ROOT"
|
||||
SPECS_DIR="$REPO_ROOT/specs"
|
||||
mkdir -p "$SPECS_DIR"
|
||||
|
||||
HIGHEST=0
|
||||
# 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" ]; then HIGHEST=$number; fi
|
||||
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")
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#
|
||||
# 5. Multi-Agent Support
|
||||
# - Handles agent-specific file paths and naming conventions
|
||||
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, 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
|
||||
# - Creates default Claude file if no agent files exist
|
||||
#
|
||||
@@ -61,7 +61,7 @@ AGENT_TYPE="${1:-}"
|
||||
# Agent-specific file paths
|
||||
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
|
||||
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
|
||||
COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
|
||||
COPILOT_FILE="$REPO_ROOT/.github/chatmodes/copilot-instructions.md"
|
||||
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
||||
QWEN_FILE="$REPO_ROOT/QWEN.md"
|
||||
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
|
||||
@@ -70,6 +70,7 @@ KILOCODE_FILE="$REPO_ROOT/.kilocode/rules/specify-rules.md"
|
||||
AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
|
||||
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
|
||||
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
|
||||
AMP_FILE="$REPO_ROOT/AGENTS.md"
|
||||
Q_FILE="$REPO_ROOT/AGENTS.md"
|
||||
|
||||
# Template file
|
||||
@@ -390,12 +391,25 @@ update_existing_agent_file() {
|
||||
new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB"
|
||||
fi
|
||||
|
||||
# Check if sections exist in the file
|
||||
local has_active_technologies=0
|
||||
local has_recent_changes=0
|
||||
|
||||
if grep -q "^## Active Technologies" "$target_file" 2>/dev/null; then
|
||||
has_active_technologies=1
|
||||
fi
|
||||
|
||||
if grep -q "^## Recent Changes" "$target_file" 2>/dev/null; then
|
||||
has_recent_changes=1
|
||||
fi
|
||||
|
||||
# Process file line by line
|
||||
local in_tech_section=false
|
||||
local in_changes_section=false
|
||||
local tech_entries_added=false
|
||||
local changes_entries_added=false
|
||||
local existing_changes_count=0
|
||||
local file_ended=false
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Handle Active Technologies section
|
||||
@@ -456,6 +470,22 @@ update_existing_agent_file() {
|
||||
# Post-loop check: if we're still in the Active Technologies section and haven't added new entries
|
||||
if [[ $in_tech_section == true ]] && [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
||||
tech_entries_added=true
|
||||
fi
|
||||
|
||||
# If sections don't exist, add them at the end of the file
|
||||
if [[ $has_active_technologies -eq 0 ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
||||
echo "" >> "$temp_file"
|
||||
echo "## Active Technologies" >> "$temp_file"
|
||||
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
||||
tech_entries_added=true
|
||||
fi
|
||||
|
||||
if [[ $has_recent_changes -eq 0 ]] && [[ -n "$new_change_entry" ]]; then
|
||||
echo "" >> "$temp_file"
|
||||
echo "## Recent Changes" >> "$temp_file"
|
||||
echo "$new_change_entry" >> "$temp_file"
|
||||
changes_entries_added=true
|
||||
fi
|
||||
|
||||
# Move temp file to target atomically
|
||||
@@ -585,12 +615,15 @@ update_specific_agent() {
|
||||
codebuddy)
|
||||
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
||||
;;
|
||||
amp)
|
||||
update_agent_file "$AMP_FILE" "Amp"
|
||||
;;
|
||||
q)
|
||||
update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown agent type '$agent_type'"
|
||||
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|q"
|
||||
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|q"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -79,15 +79,40 @@ Set-Location $repoRoot
|
||||
$specsDir = Join-Path $repoRoot 'specs'
|
||||
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
|
||||
|
||||
$highest = 0
|
||||
# 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 $highest) { $highest = $num }
|
||||
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)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh:
|
||||
2. Plan Data Extraction
|
||||
3. Agent File Management (create from template or update existing)
|
||||
4. Content Generation (technology stack, recent changes, timestamp)
|
||||
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, q)
|
||||
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, amp, q)
|
||||
|
||||
.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).
|
||||
@@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1
|
||||
#>
|
||||
param(
|
||||
[Parameter(Position=0)]
|
||||
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','q')]
|
||||
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','q')]
|
||||
[string]$AgentType
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ $NEW_PLAN = $IMPL_PLAN
|
||||
# Agent file paths
|
||||
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
|
||||
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
|
||||
$COPILOT_FILE = Join-Path $REPO_ROOT '.github/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'
|
||||
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
|
||||
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||
@@ -55,6 +55,7 @@ $KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/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'
|
||||
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
|
||||
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||
$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||
|
||||
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
|
||||
@@ -379,8 +380,9 @@ function Update-SpecificAgent {
|
||||
'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
|
||||
'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
|
||||
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
|
||||
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
|
||||
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
|
||||
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|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|q'; return $false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +415,7 @@ function Print-Summary {
|
||||
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
|
||||
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
|
||||
Write-Host ''
|
||||
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|q]'
|
||||
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 {
|
||||
|
||||
@@ -51,6 +51,7 @@ from typer.core import TyperGroup
|
||||
import readchar
|
||||
import ssl
|
||||
import truststore
|
||||
from datetime import datetime, timezone
|
||||
|
||||
ssl_context = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
client = httpx.Client(verify=ssl_context)
|
||||
@@ -64,6 +65,63 @@ def _github_auth_headers(cli_token: str | None = None) -> dict:
|
||||
token = _github_token(cli_token)
|
||||
return {"Authorization": f"Bearer {token}"} if token else {}
|
||||
|
||||
def _parse_rate_limit_headers(headers: httpx.Headers) -> dict:
|
||||
"""Extract and parse GitHub rate-limit headers."""
|
||||
info = {}
|
||||
|
||||
# Standard GitHub rate-limit headers
|
||||
if "X-RateLimit-Limit" in headers:
|
||||
info["limit"] = headers.get("X-RateLimit-Limit")
|
||||
if "X-RateLimit-Remaining" in headers:
|
||||
info["remaining"] = headers.get("X-RateLimit-Remaining")
|
||||
if "X-RateLimit-Reset" in headers:
|
||||
reset_epoch = int(headers.get("X-RateLimit-Reset", "0"))
|
||||
if reset_epoch:
|
||||
reset_time = datetime.fromtimestamp(reset_epoch, tz=timezone.utc)
|
||||
info["reset_epoch"] = reset_epoch
|
||||
info["reset_time"] = reset_time
|
||||
info["reset_local"] = reset_time.astimezone()
|
||||
|
||||
# Retry-After header (seconds or HTTP-date)
|
||||
if "Retry-After" in headers:
|
||||
retry_after = headers.get("Retry-After")
|
||||
try:
|
||||
info["retry_after_seconds"] = int(retry_after)
|
||||
except ValueError:
|
||||
# HTTP-date format - not implemented, just store as string
|
||||
info["retry_after"] = retry_after
|
||||
|
||||
return info
|
||||
|
||||
def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str) -> str:
|
||||
"""Format a user-friendly error message with rate-limit information."""
|
||||
rate_info = _parse_rate_limit_headers(headers)
|
||||
|
||||
lines = [f"GitHub API returned status {status_code} for {url}"]
|
||||
lines.append("")
|
||||
|
||||
if rate_info:
|
||||
lines.append("[bold]Rate Limit Information:[/bold]")
|
||||
if "limit" in rate_info:
|
||||
lines.append(f" • Rate Limit: {rate_info['limit']} requests/hour")
|
||||
if "remaining" in rate_info:
|
||||
lines.append(f" • Remaining: {rate_info['remaining']}")
|
||||
if "reset_local" in rate_info:
|
||||
reset_str = rate_info["reset_local"].strftime("%Y-%m-%d %H:%M:%S %Z")
|
||||
lines.append(f" • Resets at: {reset_str}")
|
||||
if "retry_after_seconds" in rate_info:
|
||||
lines.append(f" • Retry after: {rate_info['retry_after_seconds']} seconds")
|
||||
lines.append("")
|
||||
|
||||
# Add troubleshooting guidance
|
||||
lines.append("[bold]Troubleshooting Tips:[/bold]")
|
||||
lines.append(" • If you're on a shared CI or corporate environment, you may be rate-limited.")
|
||||
lines.append(" • Consider using a GitHub token via --github-token or the GH_TOKEN/GITHUB_TOKEN")
|
||||
lines.append(" environment variable to increase rate limits.")
|
||||
lines.append(" • Authenticated requests have a limit of 5,000/hour vs 60/hour for unauthenticated.")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
# Agent configuration with name, folder, install URL, and CLI tool requirement
|
||||
AGENT_CONFIG = {
|
||||
"copilot": {
|
||||
@@ -144,6 +202,12 @@ AGENT_CONFIG = {
|
||||
"install_url": "https://aws.amazon.com/developer/learning/q-developer-cli/",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"amp": {
|
||||
"name": "Amp",
|
||||
"folder": ".agents/",
|
||||
"install_url": "https://ampcode.com/manual#install",
|
||||
"requires_cli": True,
|
||||
},
|
||||
}
|
||||
|
||||
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
|
||||
@@ -571,10 +635,11 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
|
||||
)
|
||||
status = response.status_code
|
||||
if status != 200:
|
||||
msg = f"GitHub API returned {status} for {api_url}"
|
||||
# Format detailed error message with rate-limit info
|
||||
error_msg = _format_rate_limit_error(status, response.headers, api_url)
|
||||
if debug:
|
||||
msg += f"\nResponse headers: {response.headers}\nBody (truncated 500): {response.text[:500]}"
|
||||
raise RuntimeError(msg)
|
||||
error_msg += f"\n\n[dim]Response body (truncated 500):[/dim]\n{response.text[:500]}"
|
||||
raise RuntimeError(error_msg)
|
||||
try:
|
||||
release_data = response.json()
|
||||
except ValueError as je:
|
||||
@@ -621,8 +686,11 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
|
||||
headers=_github_auth_headers(github_token),
|
||||
) as response:
|
||||
if response.status_code != 200:
|
||||
body_sample = response.text[:400]
|
||||
raise RuntimeError(f"Download failed with {response.status_code}\nHeaders: {response.headers}\nBody (truncated): {body_sample}")
|
||||
# Handle rate-limiting on download as well
|
||||
error_msg = _format_rate_limit_error(response.status_code, response.headers, download_url)
|
||||
if debug:
|
||||
error_msg += f"\n\n[dim]Response body (truncated 400):[/dim]\n{response.text[:400]}"
|
||||
raise RuntimeError(error_msg)
|
||||
total_size = int(response.headers.get('content-length', 0))
|
||||
with open(zip_path, 'wb') as f:
|
||||
if total_size == 0:
|
||||
@@ -859,7 +927,7 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None =
|
||||
@app.command()
|
||||
def init(
|
||||
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, or q"),
|
||||
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"),
|
||||
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"),
|
||||
@@ -1196,6 +1264,85 @@ def check():
|
||||
if not any(agent_results.values()):
|
||||
console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]")
|
||||
|
||||
@app.command()
|
||||
def version():
|
||||
"""Display version and system information."""
|
||||
import platform
|
||||
import importlib.metadata
|
||||
|
||||
show_banner()
|
||||
|
||||
# Get CLI version from package metadata
|
||||
cli_version = "unknown"
|
||||
try:
|
||||
cli_version = importlib.metadata.version("specify-cli")
|
||||
except Exception:
|
||||
# Fallback: try reading from pyproject.toml if running from source
|
||||
try:
|
||||
import tomllib
|
||||
pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
|
||||
if pyproject_path.exists():
|
||||
with open(pyproject_path, "rb") as f:
|
||||
data = tomllib.load(f)
|
||||
cli_version = data.get("project", {}).get("version", "unknown")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fetch latest template release version
|
||||
repo_owner = "github"
|
||||
repo_name = "spec-kit"
|
||||
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/latest"
|
||||
|
||||
template_version = "unknown"
|
||||
release_date = "unknown"
|
||||
|
||||
try:
|
||||
response = client.get(
|
||||
api_url,
|
||||
timeout=10,
|
||||
follow_redirects=True,
|
||||
headers=_github_auth_headers(),
|
||||
)
|
||||
if response.status_code == 200:
|
||||
release_data = response.json()
|
||||
template_version = release_data.get("tag_name", "unknown")
|
||||
# Remove 'v' prefix if present
|
||||
if template_version.startswith("v"):
|
||||
template_version = template_version[1:]
|
||||
release_date = release_data.get("published_at", "unknown")
|
||||
if release_date != "unknown":
|
||||
# Format the date nicely
|
||||
try:
|
||||
dt = datetime.fromisoformat(release_date.replace('Z', '+00:00'))
|
||||
release_date = dt.strftime("%Y-%m-%d")
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
info_table = Table(show_header=False, box=None, padding=(0, 2))
|
||||
info_table.add_column("Key", style="cyan", justify="right")
|
||||
info_table.add_column("Value", style="white")
|
||||
|
||||
info_table.add_row("CLI Version", cli_version)
|
||||
info_table.add_row("Template Version", template_version)
|
||||
info_table.add_row("Released", release_date)
|
||||
info_table.add_row("", "")
|
||||
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("Architecture", platform.machine())
|
||||
info_table.add_row("OS Version", platform.version())
|
||||
|
||||
panel = Panel(
|
||||
info_table,
|
||||
title="[bold cyan]Specify CLI Information[/bold cyan]",
|
||||
border_style="cyan",
|
||||
padding=(1, 2)
|
||||
)
|
||||
|
||||
console.print(panel)
|
||||
console.print()
|
||||
|
||||
def main():
|
||||
app()
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
---
|
||||
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
|
||||
handoffs:
|
||||
- label: Build Technical Plan
|
||||
agent: speckit.plan
|
||||
prompt: Create a plan for the spec. I am building with...
|
||||
scripts:
|
||||
sh: scripts/bash/check-prerequisites.sh --json --paths-only
|
||||
ps: scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
---
|
||||
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync
|
||||
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
|
||||
handoffs:
|
||||
- label: Build Specification
|
||||
agent: speckit.specify
|
||||
prompt: Implement the feature specification based on the updated constitution. I want to build...
|
||||
---
|
||||
|
||||
## User Input
|
||||
|
||||
@@ -135,4 +135,3 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- Report final status with summary of completed work
|
||||
|
||||
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit.tasks` first to regenerate the task list.
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
---
|
||||
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
|
||||
handoffs:
|
||||
- label: Create Tasks
|
||||
agent: speckit.tasks
|
||||
prompt: Break the plan into tasks
|
||||
send: true
|
||||
- label: Create Checklist
|
||||
agent: speckit.checklist
|
||||
prompt: Create a checklist for the following domain...
|
||||
scripts:
|
||||
sh: scripts/bash/setup-plan.sh --json
|
||||
ps: scripts/powershell/setup-plan.ps1 -Json
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
---
|
||||
description: Create or update the feature specification from a natural language feature description.
|
||||
handoffs:
|
||||
- label: Build Technical Plan
|
||||
agent: speckit.plan
|
||||
prompt: Create a plan for the spec. I am building with...
|
||||
- label: Clarify Spec Requirements
|
||||
agent: speckit.clarify
|
||||
prompt: Clarify specification requirements
|
||||
send: true
|
||||
scripts:
|
||||
sh: scripts/bash/create-new-feature.sh --json "{ARGS}"
|
||||
ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"
|
||||
@@ -35,9 +43,9 @@ Given that feature description, do this:
|
||||
|
||||
**IMPORTANT**:
|
||||
|
||||
- Append the short-name argument to the `{SCRIPT}` command with the 2-4 word short name you created in step 1
|
||||
- Bash: `--short-name "your-generated-short-name"`
|
||||
- PowerShell: `-ShortName "your-generated-short-name"`
|
||||
- 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.
|
||||
- Bash example: `--short-name "your-generated-short-name" "Feature description here"`
|
||||
- PowerShell example: `-ShortName "your-generated-short-name" "Feature description here"`
|
||||
- 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
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
---
|
||||
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
||||
handoffs:
|
||||
- label: Analyze For Consistency
|
||||
agent: speckit.analyze
|
||||
prompt: Run a project analysis for consistency
|
||||
send: true
|
||||
- label: Implement Project
|
||||
agent: speckit.implement
|
||||
prompt: Start the implementation in phases
|
||||
send: true
|
||||
scripts:
|
||||
sh: scripts/bash/check-prerequisites.sh --json
|
||||
ps: scripts/powershell/check-prerequisites.ps1 -Json
|
||||
|
||||
31
templates/commands/taskstoissues.md
Normal file
31
templates/commands/taskstoissues.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
||||
tools: ['github/github-mcp-server/create_issue']
|
||||
scripts:
|
||||
sh: scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks
|
||||
ps: scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks
|
||||
---
|
||||
|
||||
## User Input
|
||||
|
||||
```text
|
||||
$ARGUMENTS
|
||||
```
|
||||
|
||||
You **MUST** consider the user input before proceeding (if not empty).
|
||||
|
||||
## Outline
|
||||
|
||||
1. Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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").
|
||||
1. From the executed script, extract the path to **tasks**.
|
||||
1. Get the Git remote by running:
|
||||
|
||||
```bash
|
||||
git config --get remote.origin.url
|
||||
```
|
||||
|
||||
**ONLY PROCEED TO NEXT STEPS IF THE REMOTE IS A GITHUB URL**
|
||||
|
||||
1. For each task in the list, use the GitHub MCP server to create a new issue in the repository that is representative of the Git remote.
|
||||
|
||||
**UNDER NO CIRCUMSTANCES EVER CREATE ISSUES IN REPOSITORIES THAT DO NOT MATCH THE REMOTE URL**
|
||||
Reference in New Issue
Block a user