mirror of
https://github.com/github/spec-kit.git
synced 2026-01-30 20:52:03 +00:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9111699cd2 | ||
|
|
0049b1cdc2 | ||
|
|
3e883fa32c | ||
|
|
baeb829ed4 | ||
|
|
236bcb3987 | ||
|
|
6c3d698959 | ||
|
|
4d806672d6 | ||
|
|
1c7b09d947 | ||
|
|
3963c2ef10 | ||
|
|
84e1a69edc | ||
|
|
6f22393106 | ||
|
|
5a39f63d1e | ||
|
|
964b1418a3 | ||
|
|
cad12bd2c8 | ||
|
|
8d552e6d11 | ||
|
|
ad3bb1a5fe | ||
|
|
a998d13b73 | ||
|
|
418950ad07 | ||
|
|
952e2bb5f8 | ||
|
|
56085f9323 | ||
|
|
dadda123f0 | ||
|
|
b4e1c07817 | ||
|
|
bb21eedabc | ||
|
|
f65bf6ccb7 | ||
|
|
a0ca101aa4 | ||
|
|
bf5ae42085 | ||
|
|
33df8976ca | ||
|
|
41a9fc8859 | ||
|
|
71a20eb787 | ||
|
|
798015b537 | ||
|
|
42086045cf | ||
|
|
bf6d9e8baf | ||
|
|
694de562cc | ||
|
|
45d5176d07 | ||
|
|
537f349f1b | ||
|
|
15139c1e0f | ||
|
|
f205fa3b58 | ||
|
|
4767d77ab5 | ||
|
|
c88d00d452 | ||
|
|
cf8b67e187 | ||
|
|
7c4294b163 | ||
|
|
2f58f4b9f0 | ||
|
|
7777e145d9 | ||
|
|
7e568c1201 | ||
|
|
f4fcd82920 | ||
|
|
3dcbb6e3a9 | ||
|
|
392f8b7dfa | ||
|
|
24b6d31471 | ||
|
|
960e4c0a32 | ||
|
|
df15b8e2a2 | ||
|
|
161a415274 | ||
|
|
e20d79e78b | ||
|
|
a26ee578ae | ||
|
|
d471a6fa42 | ||
|
|
bcd3f8468a | ||
|
|
d79d99f0a9 | ||
|
|
57c54d3f0a | ||
|
|
e976080cbf | ||
|
|
f438a10c7c | ||
|
|
3c4081d30f | ||
|
|
392dbf20c4 | ||
|
|
d068e1c040 | ||
|
|
0369d2a50e | ||
|
|
de2cdc633d | ||
|
|
891736ffc8 | ||
|
|
02015e512a | ||
|
|
3110452c3f | ||
|
|
c4638a936e | ||
|
|
2a7c2e9398 | ||
|
|
e6d6f3cdee | ||
|
|
598148ca67 | ||
|
|
b40b41cf50 | ||
|
|
1f3d9b5fdd | ||
|
|
f7fe48bd6a | ||
|
|
d6136cb22f | ||
|
|
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 | ||
|
|
79328aa38d | ||
|
|
f7903192a8 | ||
|
|
115b4335d9 | ||
|
|
37e87c78a0 | ||
|
|
14a574a6a8 | ||
|
|
dbd1437aea | ||
|
|
317ae4dad9 | ||
|
|
8e9d25e9be | ||
|
|
c59be99dc4 | ||
|
|
15a5630047 | ||
|
|
f3ada747cf | ||
|
|
cbc8ab020c | ||
|
|
e0e62f6757 | ||
|
|
6c22085214 | ||
|
|
02c1549f80 | ||
|
|
ab14090813 | ||
|
|
546e9d6617 | ||
|
|
33a07969c3 | ||
|
|
8de5db7a3e | ||
|
|
ea90d02c41 | ||
|
|
3e85f46465 | ||
|
|
015440838a | ||
|
|
7050a3151c | ||
|
|
9e84f46e56 | ||
|
|
5e32de1f3f | ||
|
|
5558b24475 | ||
|
|
f892b9e1cb | ||
|
|
a66af9b7f5 | ||
|
|
a5fdd53a3e | ||
|
|
ed0fa8fffe | ||
|
|
098380a46f | ||
|
|
74f7e508a4 | ||
|
|
8130d98bcc | ||
|
|
315269d9a8 | ||
|
|
71c2c63d55 | ||
|
|
b37a9516d0 | ||
|
|
b009773d5c | ||
|
|
a8514da3e8 | ||
|
|
9d4e8e9eb9 | ||
|
|
f3c77e2f4f | ||
|
|
ecec4bc5e0 | ||
|
|
900bc2ed68 | ||
|
|
03c7021270 | ||
|
|
a97374ded0 | ||
|
|
6b58824a39 | ||
|
|
b291a6efb0 | ||
|
|
3b000fce4d | ||
|
|
c59595d065 | ||
|
|
1c16a68df2 | ||
|
|
39bf3e4d9a | ||
|
|
045696641a | ||
|
|
41690cd1d4 | ||
|
|
e45c469709 | ||
|
|
8c9e586662 | ||
|
|
ce844c6259 | ||
|
|
84b46cd1b9 | ||
|
|
0cca67fcd2 | ||
|
|
66fc4c292d | ||
|
|
2baae57b26 | ||
|
|
514b0548fe | ||
|
|
be7db635cc | ||
|
|
a945077b8d | ||
|
|
7b55522213 | ||
|
|
7ca792509b | ||
|
|
4522fb4c44 | ||
|
|
36ff7e6505 | ||
|
|
defb1870da | ||
|
|
b61f04c898 | ||
|
|
64745162df | ||
|
|
97df98b9a0 | ||
|
|
36383b411f | ||
|
|
3e476c2ba6 | ||
|
|
654a00aac9 | ||
|
|
4690d13f88 | ||
|
|
e7bb98de42 | ||
|
|
d4f5c75519 | ||
|
|
09f57a87fa | ||
|
|
b702fcbbc0 | ||
|
|
2c1de4202e | ||
|
|
e65660ffc3 | ||
|
|
f7ae5781b7 | ||
|
|
d09552fc63 | ||
|
|
ed5dbf197f | ||
|
|
df4d7fa062 | ||
|
|
b4ecd14ffa | ||
|
|
26fde7cfda | ||
|
|
8abc812c57 | ||
|
|
940714df0a | ||
|
|
f393ae9825 | ||
|
|
97dee3e4bf | ||
|
|
aec568949c | ||
|
|
ed9044345b | ||
|
|
058ee510a7 | ||
|
|
e91aca54ee | ||
|
|
9c87fdd5bb | ||
|
|
301a556110 | ||
|
|
bb9ec8e638 | ||
|
|
e83d2c777d | ||
|
|
68809bdacb | ||
|
|
3cc545243b | ||
|
|
9ef389baba | ||
|
|
426ac8ab2e | ||
|
|
4de1b6b6c3 | ||
|
|
199c63901f | ||
|
|
369ed643d7 | ||
|
|
6c947cc8d8 | ||
|
|
07d506feb5 | ||
|
|
0124a0f32e | ||
|
|
e7936c3fd0 | ||
|
|
583d556677 | ||
|
|
72ed39d8a1 | ||
|
|
7c4c1edd85 | ||
|
|
5846a38c68 | ||
|
|
47e5f7c2e2 | ||
|
|
aa599b8af1 | ||
|
|
09cf4f6cc4 | ||
|
|
2b2f5a7c2a | ||
|
|
8b09559690 | ||
|
|
318b76de50 | ||
|
|
a85fdd4051 | ||
|
|
92621bca7d |
77
.devcontainer/devcontainer.json
Normal file
77
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,77 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||
{
|
||||
"name": "SpecKitDevContainer",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/python:3.13-trixie", // based on Debian "Trixie" (13)
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/common-utils:2": {
|
||||
"installZsh": true,
|
||||
"installOhMyZsh": true,
|
||||
"installOhMyZshConfig": true,
|
||||
"upgradePackages": true,
|
||||
"username": "devcontainer",
|
||||
"userUid": "automatic",
|
||||
"userGid": "automatic"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/dotnet:2": {
|
||||
"version": "lts"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git:1": {
|
||||
"ppa": true,
|
||||
"version": "latest"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/node": {
|
||||
"version": "lts"
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [
|
||||
8080 // for Spec-Kit documentation site
|
||||
],
|
||||
"containerUser": "devcontainer",
|
||||
"updateRemoteUserUID": true,
|
||||
"postCreateCommand": "chmod +x ./.devcontainer/post-create.sh && ./.devcontainer/post-create.sh",
|
||||
"postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"mhutchie.git-graph",
|
||||
"eamodio.gitlens",
|
||||
"anweber.reveal-button",
|
||||
"chrisdias.promptboost",
|
||||
// Github Copilot
|
||||
"GitHub.copilot",
|
||||
"GitHub.copilot-chat",
|
||||
// Codex
|
||||
"openai.chatgpt",
|
||||
// Kilo Code
|
||||
"kilocode.Kilo-Code",
|
||||
// Roo Code
|
||||
"RooVeterinaryInc.roo-cline",
|
||||
// Amazon Developer Q
|
||||
"AmazonWebServices.amazon-q-vscode",
|
||||
// Claude Code
|
||||
"anthropic.claude-code"
|
||||
],
|
||||
"settings": {
|
||||
"debug.javascript.autoAttachFilter": "disabled", // fix running commands in integrated terminal
|
||||
|
||||
// Specify settings for Github Copilot
|
||||
"git.autofetch": true,
|
||||
"chat.promptFilesRecommendations": {
|
||||
"speckit.constitution": true,
|
||||
"speckit.specify": true,
|
||||
"speckit.plan": true,
|
||||
"speckit.tasks": true,
|
||||
"speckit.implement": true
|
||||
},
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
".specify/scripts/bash/": true,
|
||||
".specify/scripts/powershell/": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
.devcontainer/post-create.sh
Executable file
100
.devcontainer/post-create.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit immediately on error, treat unset variables as an error, and fail if any command in a pipeline fails.
|
||||
set -euo pipefail
|
||||
|
||||
# Function to run a command and show logs only on error
|
||||
run_command() {
|
||||
local command_to_run="$*"
|
||||
local output
|
||||
local exit_code
|
||||
|
||||
# Capture all output (stdout and stderr)
|
||||
output=$(eval "$command_to_run" 2>&1) || exit_code=$?
|
||||
exit_code=${exit_code:-0}
|
||||
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo -e "\033[0;31m[ERROR] Command failed (Exit Code $exit_code): $command_to_run\033[0m" >&2
|
||||
echo -e "\033[0;31m$output\033[0m" >&2
|
||||
|
||||
exit $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Installing CLI-based AI Agents
|
||||
|
||||
echo -e "\n🤖 Installing Copilot CLI..."
|
||||
run_command "npm install -g @github/copilot@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Claude CLI..."
|
||||
run_command "npm install -g @anthropic-ai/claude-code@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Codex CLI..."
|
||||
run_command "npm install -g @openai/codex@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Gemini CLI..."
|
||||
run_command "npm install -g @google/gemini-cli@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Augie CLI..."
|
||||
run_command "npm install -g @augmentcode/auggie@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Qwen Code CLI..."
|
||||
run_command "npm install -g @qwen-code/qwen-code@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing OpenCode CLI..."
|
||||
run_command "npm install -g opencode-ai@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Amazon Q CLI..."
|
||||
# 👉🏾 https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-verify-download.html
|
||||
|
||||
run_command "curl --proto '=https' --tlsv1.2 -sSf 'https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip' -o 'q.zip'"
|
||||
run_command "curl --proto '=https' --tlsv1.2 -sSf 'https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip.sig' -o 'q.zip.sig'"
|
||||
cat > amazonq-public-key.asc << 'EOF'
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEZig60RYJKwYBBAHaRw8BAQdAy/+G05U5/EOA72WlcD4WkYn5SInri8pc4Z6D
|
||||
BKNNGOm0JEFtYXpvbiBRIENMSSBUZWFtIDxxLWNsaUBhbWF6b24uY29tPoiZBBMW
|
||||
CgBBFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcFAmYoOtECGwMFCQPCZwAFCwkIBwIC
|
||||
IgIGFQoJCAsCBBYCAwECHgcCF4AACgkQUNx6jcJMVmef5QD/QWWEGG/cOnbDnp68
|
||||
SJXuFkwiNwlH2rPw9ZRIQMnfAS0A/0V6ZsGB4kOylBfc7CNfzRFGtovdBBgHqA6P
|
||||
zQ/PNscGuDgEZig60RIKKwYBBAGXVQEFAQEHQC4qleONMBCq3+wJwbZSr0vbuRba
|
||||
D1xr4wUPn4Avn4AnAwEIB4h+BBgWCgAmFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcF
|
||||
AmYoOtECGwwFCQPCZwAACgkQUNx6jcJMVmchMgEA6l3RveCM0YHAGQaSFMkguoAo
|
||||
vK6FgOkDawgP0NPIP2oA/jIAO4gsAntuQgMOsPunEdDeji2t+AhV02+DQIsXZpoB
|
||||
=f8yY
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
EOF
|
||||
run_command "gpg --batch --import amazonq-public-key.asc"
|
||||
run_command "gpg --verify q.zip.sig q.zip"
|
||||
run_command "unzip -q q.zip"
|
||||
run_command "chmod +x ./q/install.sh"
|
||||
run_command "./q/install.sh --no-confirm"
|
||||
run_command "rm -rf ./q q.zip q.zip.sig amazonq-public-key.asc"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing CodeBuddy CLI..."
|
||||
run_command "npm install -g @tencent-ai/codebuddy-code@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
# Installing UV (Python package manager)
|
||||
echo -e "\n🐍 Installing UV - Python Package Manager..."
|
||||
run_command "pipx install uv"
|
||||
echo "✅ Done"
|
||||
|
||||
# Installing DocFx (for documentation site)
|
||||
echo -e "\n📚 Installing DocFx..."
|
||||
run_command "dotnet tool update -g docfx"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🧹 Cleaning cache..."
|
||||
run_command "sudo apt-get autoclean"
|
||||
run_command "sudo apt-get clean"
|
||||
|
||||
echo "✅ Setup completed. Happy coding! 🚀"
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1,2 +1,3 @@
|
||||
# Global code owner
|
||||
* @localden
|
||||
|
||||
|
||||
1
.github/workflows/docs.yml
vendored
1
.github/workflows/docs.yml
vendored
@@ -65,3 +65,4 @@ jobs:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
|
||||
20
.github/workflows/lint.yml
vendored
Normal file
20
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Lint
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
markdownlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run markdownlint-cli2
|
||||
uses: DavidAnson/markdownlint-cli2-action@v19
|
||||
with:
|
||||
globs: '**/*.md'
|
||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -57,3 +57,4 @@ jobs:
|
||||
run: |
|
||||
chmod +x .github/workflows/scripts/update-version.sh
|
||||
.github/workflows/scripts/update-version.sh ${{ steps.get_tag.outputs.new_version }}
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@ if gh release view "$VERSION" >/dev/null 2>&1; then
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Release $VERSION does not exist, proceeding..."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -22,8 +22,8 @@ gh release create "$VERSION" \
|
||||
.genreleases/spec-kit-template-claude-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-gemini-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-gemini-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-cursor-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-cursor-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-cursor-agent-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-cursor-agent-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-opencode-sh-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-opencode-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-qwen-sh-"$VERSION".zip \
|
||||
@@ -38,7 +38,17 @@ gh release create "$VERSION" \
|
||||
.genreleases/spec-kit-template-auggie-ps-"$VERSION".zip \
|
||||
.genreleases/spec-kit-template-roo-sh-"$VERSION".zip \
|
||||
.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-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-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-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" \
|
||||
--notes-file release_notes.md
|
||||
--notes-file release_notes.md
|
||||
|
||||
424
.github/workflows/scripts/create-release-packages.ps1
vendored
Normal file
424
.github/workflows/scripts/create-release-packages.ps1
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
#!/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)"
|
||||
}
|
||||
66
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file → Executable file
66
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file → Executable file
@@ -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 qwen opencode windsurf codex (default: all)
|
||||
# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp shai bob (default: all)
|
||||
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
|
||||
# Examples:
|
||||
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
|
||||
@@ -91,15 +91,36 @@ generate_commands() {
|
||||
|
||||
case $ext in
|
||||
toml)
|
||||
body=$(printf '%s\n' "$body" | sed 's/\\/\\\\/g')
|
||||
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
|
||||
md)
|
||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||
prompt.md)
|
||||
agent.md)
|
||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
generate_copilot_prompts() {
|
||||
local agents_dir=$1 prompts_dir=$2
|
||||
mkdir -p "$prompts_dir"
|
||||
|
||||
# Generate a .prompt.md file for each .agent.md file
|
||||
for agent_file in "$agents_dir"/speckit.*.agent.md; do
|
||||
[[ -f "$agent_file" ]] || continue
|
||||
|
||||
local basename=$(basename "$agent_file" .agent.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}"
|
||||
@@ -132,7 +153,7 @@ build_variant() {
|
||||
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -not -name "vscode-settings.json" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }
|
||||
|
||||
# NOTE: We substitute {ARGS} internally. Outward tokens differ intentionally:
|
||||
# * Markdown/prompt (claude, copilot, cursor, opencode): $ARGUMENTS
|
||||
# * Markdown/prompt (claude, copilot, cursor-agent, opencode): $ARGUMENTS
|
||||
# * TOML (gemini, qwen): {{args}}
|
||||
# This keeps formats readable without extra abstraction.
|
||||
|
||||
@@ -145,15 +166,17 @@ 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/agents"
|
||||
generate_commands copilot agent.md "\$ARGUMENTS" "$base_dir/.github/agents" "$script"
|
||||
# Generate companion prompt files
|
||||
generate_copilot_prompts "$base_dir/.github/agents" "$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"
|
||||
;;
|
||||
cursor)
|
||||
cursor-agent)
|
||||
mkdir -p "$base_dir/.cursor/commands"
|
||||
generate_commands cursor md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
|
||||
generate_commands cursor-agent md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
|
||||
qwen)
|
||||
mkdir -p "$base_dir/.qwen/commands"
|
||||
generate_commands qwen toml "{{args}}" "$base_dir/.qwen/commands" "$script"
|
||||
@@ -176,36 +199,50 @@ build_variant() {
|
||||
roo)
|
||||
mkdir -p "$base_dir/.roo/commands"
|
||||
generate_commands roo md "\$ARGUMENTS" "$base_dir/.roo/commands" "$script" ;;
|
||||
codebuddy)
|
||||
mkdir -p "$base_dir/.codebuddy/commands"
|
||||
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)
|
||||
mkdir -p "$base_dir/.agents/commands"
|
||||
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)
|
||||
mkdir -p "$base_dir/.amazonq/prompts"
|
||||
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
|
||||
( 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"
|
||||
}
|
||||
|
||||
# Determine agent list
|
||||
ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf codex kilocode auggie roo q)
|
||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q bob qoder)
|
||||
ALL_SCRIPTS=(sh ps)
|
||||
|
||||
|
||||
norm_list() {
|
||||
# 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?" ":"") $i)}}}END{printf("\n")}'
|
||||
# convert comma+space separated -> line 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")}'
|
||||
}
|
||||
|
||||
validate_subset() {
|
||||
local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
|
||||
local ok=1
|
||||
local invalid=0
|
||||
for it in "${items[@]}"; do
|
||||
local found=0
|
||||
for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
|
||||
if [[ $found -eq 0 ]]; then
|
||||
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
|
||||
ok=0
|
||||
invalid=1
|
||||
fi
|
||||
done
|
||||
return $ok
|
||||
return $invalid
|
||||
}
|
||||
|
||||
if [[ -n ${AGENTS:-} ]]; then
|
||||
@@ -233,3 +270,4 @@ done
|
||||
|
||||
echo "Archives in $GENRELEASES_DIR:"
|
||||
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ 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:"
|
||||
cat release_notes.md
|
||||
cat release_notes.md
|
||||
|
||||
@@ -21,4 +21,4 @@ PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "New version will be: $NEW_VERSION"
|
||||
echo "New version will be: $NEW_VERSION"
|
||||
|
||||
2
.github/workflows/scripts/update-version.sh
vendored
2
.github/workflows/scripts/update-version.sh
vendored
@@ -20,4 +20,4 @@ if [ -f "pyproject.toml" ]; then
|
||||
echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
|
||||
else
|
||||
echo "Warning: pyproject.toml not found, skipping version update"
|
||||
fi
|
||||
fi
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -32,6 +32,7 @@ env/
|
||||
*.swp
|
||||
*.swo
|
||||
.DS_Store
|
||||
*.tmp
|
||||
|
||||
# Project specific
|
||||
*.log
|
||||
@@ -42,4 +43,4 @@ env/
|
||||
# Spec Kit-specific files
|
||||
.genreleases/
|
||||
*.zip
|
||||
sdd-*/
|
||||
sdd-*/
|
||||
|
||||
27
.markdownlint-cli2.jsonc
Normal file
27
.markdownlint-cli2.jsonc
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
// https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
|
||||
"config": {
|
||||
"default": true,
|
||||
"MD003": {
|
||||
"style": "atx"
|
||||
},
|
||||
"MD007": {
|
||||
"indent": 2
|
||||
},
|
||||
"MD013": false,
|
||||
"MD024": {
|
||||
"siblings_only": true
|
||||
},
|
||||
"MD033": false,
|
||||
"MD041": false,
|
||||
"MD049": {
|
||||
"style": "asterisk"
|
||||
},
|
||||
"MD050": {
|
||||
"style": "asterisk"
|
||||
}
|
||||
},
|
||||
"ignores": [
|
||||
".genreleases/"
|
||||
]
|
||||
}
|
||||
255
AGENTS.md
255
AGENTS.md
@@ -29,64 +29,71 @@ Specify supports multiple AI agents by generating agent-specific command files a
|
||||
|
||||
### Current Supported Agents
|
||||
|
||||
| Agent | Directory | Format | CLI Tool | Description |
|
||||
|-------|-----------|---------|----------|-------------|
|
||||
| **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 |
|
||||
| **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 |
|
||||
| **Windsurf** | `.windsurf/workflows/` | Markdown | N/A (IDE-based) | Windsurf IDE workflows |
|
||||
| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI |
|
||||
|
||||
| Agent | Directory | Format | CLI Tool | Description |
|
||||
| -------------------------- | ---------------------- | -------- | --------------- | --------------------------- |
|
||||
| **Claude Code** | `.claude/commands/` | Markdown | `claude` | Anthropic's Claude Code 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 |
|
||||
| **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 |
|
||||
| **Codex CLI** | `.codex/commands/` | Markdown | `codex` | Codex CLI |
|
||||
| **Windsurf** | `.windsurf/workflows/` | Markdown | N/A (IDE-based) | Windsurf IDE workflows |
|
||||
| **Kilo Code** | `.kilocode/rules/` | Markdown | N/A (IDE-based) | Kilo Code IDE |
|
||||
| **Auggie CLI** | `.augment/rules/` | Markdown | `auggie` | Auggie CLI |
|
||||
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
|
||||
| **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 |
|
||||
| **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
|
||||
|
||||
Follow these steps to add a new agent (using Windsurf as an example):
|
||||
Follow these steps to add a new agent (using a hypothetical new agent as an example):
|
||||
|
||||
#### 1. Update AI_CHOICES Constant
|
||||
#### 1. Add to AGENT_CONFIG
|
||||
|
||||
Add the new agent to the `AI_CHOICES` dictionary in `src/specify_cli/__init__.py`:
|
||||
**IMPORTANT**: Use the actual CLI tool name as the key, not a shortened version.
|
||||
|
||||
Add the new agent to the `AGENT_CONFIG` dictionary in `src/specify_cli/__init__.py`. This is the **single source of truth** for all agent metadata:
|
||||
|
||||
```python
|
||||
AI_CHOICES = {
|
||||
"copilot": "GitHub Copilot",
|
||||
"claude": "Claude Code",
|
||||
"gemini": "Gemini CLI",
|
||||
"cursor": "Cursor",
|
||||
"qwen": "Qwen Code",
|
||||
"opencode": "opencode",
|
||||
"windsurf": "Windsurf",
|
||||
"q": "Amazon Q Developer CLI" # Add new agent here
|
||||
AGENT_CONFIG = {
|
||||
# ... existing agents ...
|
||||
"new-agent-cli": { # Use the ACTUAL CLI tool name (what users type in terminal)
|
||||
"name": "New Agent Display Name",
|
||||
"folder": ".newagent/", # Directory for agent files
|
||||
"install_url": "https://example.com/install", # URL for installation docs (or None if IDE-based)
|
||||
"requires_cli": True, # True if CLI tool required, False for IDE-based agents
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Also update the `agent_folder_map` in the same file to include the new agent's folder for the security notice:
|
||||
**Key Design Principle**: The dictionary key should match the actual executable name that users install. For example:
|
||||
|
||||
```python
|
||||
agent_folder_map = {
|
||||
"claude": ".claude/",
|
||||
"gemini": ".gemini/",
|
||||
"cursor": ".cursor/",
|
||||
"qwen": ".qwen/",
|
||||
"opencode": ".opencode/",
|
||||
"codex": ".codex/",
|
||||
"windsurf": ".windsurf/",
|
||||
"kilocode": ".kilocode/",
|
||||
"auggie": ".auggie/",
|
||||
"copilot": ".github/",
|
||||
"q": ".amazonq/" # Add new agent folder here
|
||||
}
|
||||
```
|
||||
- ✅ Use `"cursor-agent"` because the CLI tool is literally called `cursor-agent`
|
||||
- ❌ Don't use `"cursor"` as a shortcut if the tool is `cursor-agent`
|
||||
|
||||
This eliminates the need for special-case mappings throughout the codebase.
|
||||
|
||||
**Field Explanations**:
|
||||
|
||||
- `name`: Human-readable display name shown to users
|
||||
- `folder`: Directory where agent-specific files are stored (relative to project root)
|
||||
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
|
||||
- `requires_cli`: Whether the agent requires a CLI tool check during initialization
|
||||
|
||||
#### 2. Update CLI Help Text
|
||||
|
||||
Update all help text and examples to include the new agent:
|
||||
Update the `--ai` parameter help text in the `init()` command to include the new agent:
|
||||
|
||||
- Command option help: `--ai` parameter description
|
||||
- Function docstrings and examples
|
||||
- Error messages with agent lists
|
||||
```python
|
||||
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, new-agent-cli, or q"),
|
||||
```
|
||||
|
||||
Also update any function docstrings, examples, and error messages that list available agents.
|
||||
|
||||
#### 3. Update README Documentation
|
||||
|
||||
@@ -101,12 +108,14 @@ Update the **Supported AI Agents** section in `README.md` to include the new age
|
||||
|
||||
Modify `.github/workflows/scripts/create-release-packages.sh`:
|
||||
|
||||
##### Add to ALL_AGENTS array:
|
||||
##### Add to ALL_AGENTS array
|
||||
|
||||
```bash
|
||||
ALL_AGENTS=(claude gemini copilot cursor qwen opencode windsurf q)
|
||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf q)
|
||||
```
|
||||
|
||||
##### Add case statement for directory structure:
|
||||
##### Add case statement for directory structure
|
||||
|
||||
```bash
|
||||
case $agent in
|
||||
# ... existing cases ...
|
||||
@@ -130,19 +139,21 @@ gh release create "$VERSION" \
|
||||
|
||||
#### 5. Update Agent Context Scripts
|
||||
|
||||
##### Bash script (`scripts/bash/update-agent-context.sh`):
|
||||
##### Bash script (`scripts/bash/update-agent-context.sh`)
|
||||
|
||||
Add file variable:
|
||||
|
||||
```bash
|
||||
WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md"
|
||||
```
|
||||
|
||||
Add to case statement:
|
||||
|
||||
```bash
|
||||
case "$AGENT_TYPE" in
|
||||
# ... existing cases ...
|
||||
windsurf) update_agent_file "$WINDSURF_FILE" "Windsurf" ;;
|
||||
"")
|
||||
"")
|
||||
# ... existing checks ...
|
||||
[ -f "$WINDSURF_FILE" ] && update_agent_file "$WINDSURF_FILE" "Windsurf";
|
||||
# Update default creation condition
|
||||
@@ -150,14 +161,16 @@ case "$AGENT_TYPE" in
|
||||
esac
|
||||
```
|
||||
|
||||
##### PowerShell script (`scripts/powershell/update-agent-context.ps1`):
|
||||
##### PowerShell script (`scripts/powershell/update-agent-context.ps1`)
|
||||
|
||||
Add file variable:
|
||||
|
||||
```powershell
|
||||
$windsurfFile = Join-Path $repoRoot '.windsurf/rules/specify-rules.md'
|
||||
```
|
||||
|
||||
Add to switch statement:
|
||||
|
||||
```powershell
|
||||
switch ($AgentType) {
|
||||
# ... existing cases ...
|
||||
@@ -190,27 +203,135 @@ elif selected_ai == "windsurf":
|
||||
agent_tool_missing = True
|
||||
```
|
||||
|
||||
**Note**: Skip CLI checks for IDE-based agents (Copilot, Windsurf).
|
||||
**Note**: CLI tool checks are now handled automatically based on the `requires_cli` field in AGENT_CONFIG. No additional code changes needed in the `check()` or `init()` commands - they automatically loop through AGENT_CONFIG and check tools as needed.
|
||||
|
||||
## Important Design Decisions
|
||||
|
||||
### Using Actual CLI Tool Names as Keys
|
||||
|
||||
**CRITICAL**: When adding a new agent to AGENT_CONFIG, always use the **actual executable name** as the dictionary key, not a shortened or convenient version.
|
||||
|
||||
**Why this matters:**
|
||||
|
||||
- The `check_tool()` function uses `shutil.which(tool)` to find executables in the system PATH
|
||||
- If the key doesn't match the actual CLI tool name, you'll need special-case mappings throughout the codebase
|
||||
- This creates unnecessary complexity and maintenance burden
|
||||
|
||||
**Example - The Cursor Lesson:**
|
||||
|
||||
❌ **Wrong approach** (requires special-case mapping):
|
||||
|
||||
```python
|
||||
AGENT_CONFIG = {
|
||||
"cursor": { # Shorthand that doesn't match the actual tool
|
||||
"name": "Cursor",
|
||||
# ...
|
||||
}
|
||||
}
|
||||
|
||||
# Then you need special cases everywhere:
|
||||
cli_tool = agent_key
|
||||
if agent_key == "cursor":
|
||||
cli_tool = "cursor-agent" # Map to the real tool name
|
||||
```
|
||||
|
||||
✅ **Correct approach** (no mapping needed):
|
||||
|
||||
```python
|
||||
AGENT_CONFIG = {
|
||||
"cursor-agent": { # Matches the actual executable name
|
||||
"name": "Cursor",
|
||||
# ...
|
||||
}
|
||||
}
|
||||
|
||||
# No special cases needed - just use agent_key directly!
|
||||
```
|
||||
|
||||
**Benefits of this approach:**
|
||||
|
||||
- Eliminates special-case logic scattered throughout the codebase
|
||||
- Makes the code more maintainable and easier to understand
|
||||
- Reduces the chance of bugs when adding new agents
|
||||
- Tool checking "just works" without additional mappings
|
||||
|
||||
#### 7. Update Devcontainer files (Optional)
|
||||
|
||||
For agents that have VS Code extensions or require CLI installation, update the devcontainer configuration files:
|
||||
|
||||
##### VS Code Extension-based Agents
|
||||
|
||||
For agents available as VS Code extensions, add them to `.devcontainer/devcontainer.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
// ... existing extensions ...
|
||||
// [New Agent Name]
|
||||
"[New Agent Extension ID]"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### CLI-based Agents
|
||||
|
||||
For agents that require CLI tools, add installation commands to `.devcontainer/post-create.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Existing installations...
|
||||
|
||||
echo -e "\n🤖 Installing [New Agent Name] CLI..."
|
||||
# run_command "npm install -g [agent-cli-package]@latest" # Example for node-based CLI
|
||||
# or other installation instructions (must be non-interactive and compatible with Linux Debian "Trixie" or later)...
|
||||
echo "✅ Done"
|
||||
|
||||
```
|
||||
|
||||
**Quick Tips:**
|
||||
|
||||
- **Extension-based agents**: Add to the `extensions` array in `devcontainer.json`
|
||||
- **CLI-based agents**: Add installation scripts to `post-create.sh`
|
||||
- **Hybrid agents**: May require both extension and CLI installation
|
||||
- **Test thoroughly**: Ensure installations work in the devcontainer environment
|
||||
|
||||
## Agent Categories
|
||||
|
||||
### CLI-Based Agents
|
||||
|
||||
Require a command-line tool to be installed:
|
||||
|
||||
- **Claude Code**: `claude` CLI
|
||||
- **Gemini CLI**: `gemini` CLI
|
||||
- **Gemini CLI**: `gemini` CLI
|
||||
- **Cursor**: `cursor-agent` CLI
|
||||
- **Qwen Code**: `qwen` CLI
|
||||
- **opencode**: `opencode` CLI
|
||||
- **Amazon Q Developer CLI**: `q` CLI
|
||||
- **CodeBuddy CLI**: `codebuddy` CLI
|
||||
- **Qoder CLI**: `qoder` CLI
|
||||
- **Amp**: `amp` CLI
|
||||
- **SHAI**: `shai` CLI
|
||||
|
||||
### IDE-Based Agents
|
||||
|
||||
Work within integrated development environments:
|
||||
|
||||
- **GitHub Copilot**: Built into VS Code/compatible editors
|
||||
- **Windsurf**: Built into Windsurf IDE
|
||||
- **IBM Bob**: Built into IBM Bob IDE
|
||||
|
||||
## Command File Formats
|
||||
|
||||
### Markdown Format
|
||||
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer
|
||||
|
||||
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp, SHAI, IBM Bob
|
||||
|
||||
**Standard format:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
@@ -220,7 +341,19 @@ 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
|
||||
|
||||
```toml
|
||||
@@ -235,13 +368,14 @@ Command content with {SCRIPT} and {{args}} placeholders.
|
||||
|
||||
- **CLI agents**: Usually `.<agent-name>/commands/`
|
||||
- **IDE agents**: Follow IDE-specific patterns:
|
||||
- Copilot: `.github/prompts/`
|
||||
- Copilot: `.github/agents/`
|
||||
- Cursor: `.cursor/commands/`
|
||||
- Windsurf: `.windsurf/workflows/`
|
||||
|
||||
## Argument Patterns
|
||||
|
||||
Different agents use different argument placeholders:
|
||||
|
||||
- **Markdown/prompt-based**: `$ARGUMENTS`
|
||||
- **TOML-based**: `{{args}}`
|
||||
- **Script placeholders**: `{SCRIPT}` (replaced with actual script path)
|
||||
@@ -257,20 +391,23 @@ Different agents use different argument placeholders:
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Forgetting update scripts**: Both bash and PowerShell scripts must be updated
|
||||
2. **Missing CLI checks**: Only add for agents that actually have CLI tools
|
||||
3. **Wrong argument format**: Use correct placeholder format for each agent type
|
||||
4. **Directory naming**: Follow agent-specific conventions exactly
|
||||
5. **Help text inconsistency**: Update all user-facing text consistently
|
||||
1. **Using shorthand keys instead of actual CLI tool names**: Always use the actual executable name as the AGENT_CONFIG key (e.g., `"cursor-agent"` not `"cursor"`). This prevents the need for special-case mappings throughout the codebase.
|
||||
2. **Forgetting update scripts**: Both bash and PowerShell scripts must be updated when adding new agents.
|
||||
3. **Incorrect `requires_cli` value**: Set to `True` only for agents that actually have CLI tools to check; set to `False` for IDE-based agents.
|
||||
4. **Wrong argument format**: Use correct placeholder format for each agent type (`$ARGUMENTS` for Markdown, `{{args}}` for TOML).
|
||||
5. **Directory naming**: Follow agent-specific conventions exactly (check existing agents for patterns).
|
||||
6. **Help text inconsistency**: Update all user-facing text consistently (help strings, docstrings, README, error messages).
|
||||
|
||||
## Future Considerations
|
||||
|
||||
When adding new agents:
|
||||
|
||||
- Consider the agent's native command/workflow patterns
|
||||
- Ensure compatibility with the Spec-Driven Development process
|
||||
- Document any special requirements or limitations
|
||||
- Update this guide with lessons learned
|
||||
- Verify the actual CLI tool name before adding to AGENT_CONFIG
|
||||
|
||||
---
|
||||
|
||||
*This documentation should be updated whenever new agents are added to maintain accuracy and completeness.*
|
||||
*This documentation should be updated whenever new agents are added to maintain accuracy and completeness.*
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -7,6 +7,58 @@ 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.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
|
||||
|
||||
- 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
|
||||
|
||||
- **Intelligent Branch Naming**: `create-new-feature` scripts now support `--short-name` parameter for custom branch names
|
||||
- When `--short-name` provided: Uses the custom name directly (cleaned and formatted)
|
||||
- When omitted: Automatically generates meaningful names using stop word filtering and length-based filtering
|
||||
- Filters out common stop words (I, want, to, the, for, etc.)
|
||||
- Removes words shorter than 3 characters (unless they're uppercase acronyms)
|
||||
- Takes 3-4 most meaningful words from the description
|
||||
- **Enforces GitHub's 244-byte branch name limit** with automatic truncation and warnings
|
||||
- Examples:
|
||||
- "I want to create user authentication" → `001-create-user-authentication`
|
||||
- "Implement OAuth2 integration for API" → `001-implement-oauth2-integration-api`
|
||||
- "Fix payment processing bug" → `001-fix-payment-processing`
|
||||
- Very long descriptions are automatically truncated at word boundaries to stay within limits
|
||||
- Designed for AI agents to provide semantic short names while maintaining standalone usability
|
||||
|
||||
### Changed
|
||||
|
||||
- Enhanced help documentation for `create-new-feature.sh` and `create-new-feature.ps1` scripts with examples
|
||||
- Branch names now validated against GitHub's 244-byte limit with automatic truncation if needed
|
||||
|
||||
## [0.0.19] - 2025-10-10
|
||||
|
||||
### Added
|
||||
|
||||
- Support for CodeBuddy (thank you to [@lispking](https://github.com/lispking) for the contribution).
|
||||
- You can now see Git-sourced errors in the Specify CLI.
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed the path to the constitution in `plan.md` (thank you to [@lyzno1](https://github.com/lyzno1) for spotting).
|
||||
- Fixed backslash escapes in generated TOML files for Gemini (thank you to [@hsin19](https://github.com/hsin19) for the contribution).
|
||||
- Implementation command now ensures that the correct ignore files are added (thank you to [@sigent-amazon](https://github.com/sigent-amazon) for the contribution).
|
||||
|
||||
## [0.0.18] - 2025-10-06
|
||||
|
||||
### Added
|
||||
@@ -27,7 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- New `/clarify` command template to surface up to 5 targeted clarification questions for an existing spec and persist answers into a Clarifications section in the spec.
|
||||
- New `/analyze` command template providing a non-destructive cross-artifact discrepancy and alignment report (spec, clarifications, plan, tasks, constitution) inserted after `/tasks` and before `/implement`.
|
||||
- Note: Constitution rules are explicitly treated as non-negotiable; any conflict is a CRITICAL finding requiring artifact remediation, not weakening of principles.
|
||||
- Note: Constitution rules are explicitly treated as non-negotiable; any conflict is a CRITICAL finding requiring artifact remediation, not weakening of principles.
|
||||
|
||||
## [0.0.16] - 2025-09-22
|
||||
|
||||
@@ -104,7 +156,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Updated command instructions in the CLI.
|
||||
- Cleaned up the code to not render agent-specific information when it's generic.
|
||||
|
||||
|
||||
## [0.0.6] - 2025-09-17
|
||||
|
||||
### Added
|
||||
|
||||
@@ -14,21 +14,21 @@ orientation.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
@@ -71,4 +71,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Contributing to Spec Kit
|
||||
# Contributing to Spec Kit
|
||||
|
||||
Hi there! We're thrilled that you'd like to contribute to Spec Kit. Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
|
||||
|
||||
@@ -13,10 +13,27 @@ These are one time installations required to be able to test your changes locall
|
||||
1. Install [Git](https://git-scm.com/downloads)
|
||||
1. Have an [AI coding agent available](README.md#-supported-ai-agents)
|
||||
|
||||
<details>
|
||||
<summary><b>💡 Hint if you are using <code>VSCode</code> or <code>GitHub Codespaces</code> as your IDE</b></summary>
|
||||
|
||||
<br>
|
||||
|
||||
Provided you have [Docker](https://docker.com) installed on your machine, you can leverage [Dev Containers](https://containers.dev) through this [VSCode extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), to easily set up your development environment, with aforementioned tools already installed and configured, thanks to the `.devcontainer/devcontainer.json` file (located at the root of the project).
|
||||
|
||||
To do so, simply:
|
||||
|
||||
- Checkout the repo
|
||||
- Open it with VSCode
|
||||
- Open the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) and select "Dev Containers: Open Folder in Container..."
|
||||
|
||||
On [GitHub Codespaces](https://github.com/features/codespaces) it's even simpler, as it leverages the `.devcontainer/devcontainer.json` automatically upon opening the codespace.
|
||||
|
||||
</details>
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
>[!NOTE]
|
||||
>If your pull request introduces a large change that materially impacts the work of the CLI or the rest of the repository (e.g., you're introducing new templates, arguments, or otherwise major changes), make sure that it was **discussed and agreed upon** by the project maintainers. Pull requests with large changes that did not have a prior conversation and agreement will be closed.
|
||||
> [!NOTE]
|
||||
> If your pull request introduces a large change that materially impacts the work of the CLI or the rest of the repository (e.g., you're introducing new templates, arguments, or otherwise major changes), make sure that it was **discussed and agreed upon** by the project maintainers. Pull requests with large changes that did not have a prior conversation and agreement will be closed.
|
||||
|
||||
1. Fork and clone the repository
|
||||
1. Configure and install the dependencies: `uv sync`
|
||||
@@ -45,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:
|
||||
|
||||
```bash
|
||||
./.github/workflows/scripts/create-release-packages.sh v1.0.0
|
||||
```
|
||||
|
||||
2. **Copy the relevant package to your test project**
|
||||
|
||||
```bash
|
||||
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]
|
||||
@@ -82,7 +122,7 @@ When submitting AI-assisted contributions, please ensure they include:
|
||||
|
||||
- **Clear disclosure of AI use** - You are transparent about AI use and degree to which you're using it for the contribution
|
||||
- **Human understanding and testing** - You've personally tested the changes and understand what they do
|
||||
- **Clear rationale** - You can explain why the change is needed and how it fits within Spec Kit's goals
|
||||
- **Clear rationale** - You can explain why the change is needed and how it fits within Spec Kit's goals
|
||||
- **Concrete evidence** - Include test cases, scenarios, or examples that demonstrate the improvement
|
||||
- **Your own analysis** - Share your thoughts on the end-to-end developer experience
|
||||
|
||||
|
||||
1
LICENSE
1
LICENSE
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
259
README.md
259
README.md
@@ -1,11 +1,11 @@
|
||||
<div align="center">
|
||||
<img src="./media/logo_small.webp"/>
|
||||
<img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
|
||||
<h1>🌱 Spec Kit</h1>
|
||||
<h3><em>Build high-quality software faster.</em></h3>
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<strong>An effort to allow organizations to focus on product scenarios rather than writing undifferentiated code with the help of Spec-Driven Development.</strong>
|
||||
<strong>An open source toolkit that allows you to focus on product scenarios and predictable outcomes instead of vibe coding every piece from scratch.</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -20,16 +20,16 @@
|
||||
## Table of Contents
|
||||
|
||||
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
||||
- [⚡ Get started](#-get-started)
|
||||
- [⚡ Get Started](#-get-started)
|
||||
- [📽️ Video Overview](#️-video-overview)
|
||||
- [🤖 Supported AI Agents](#-supported-ai-agents)
|
||||
- [🔧 Specify CLI Reference](#-specify-cli-reference)
|
||||
- [📚 Core philosophy](#-core-philosophy)
|
||||
- [🌟 Development phases](#-development-phases)
|
||||
- [🎯 Experimental goals](#-experimental-goals)
|
||||
- [📚 Core Philosophy](#-core-philosophy)
|
||||
- [🌟 Development Phases](#-development-phases)
|
||||
- [🎯 Experimental Goals](#-experimental-goals)
|
||||
- [🔧 Prerequisites](#-prerequisites)
|
||||
- [📖 Learn more](#-learn-more)
|
||||
- [📋 Detailed process](#-detailed-process)
|
||||
- [📖 Learn More](#-learn-more)
|
||||
- [📋 Detailed Process](#-detailed-process)
|
||||
- [🔍 Troubleshooting](#-troubleshooting)
|
||||
- [👥 Maintainers](#-maintainers)
|
||||
- [💬 Support](#-support)
|
||||
@@ -40,9 +40,9 @@
|
||||
|
||||
Spec-Driven Development **flips the script** on traditional software development. For decades, code has been king — specifications were just scaffolding we built and discarded once the "real work" of coding began. Spec-Driven Development changes this: **specifications become executable**, directly generating working implementations rather than just guiding them.
|
||||
|
||||
## ⚡ Get started
|
||||
## ⚡ Get Started
|
||||
|
||||
### 1. Install Specify
|
||||
### 1. Install Specify CLI
|
||||
|
||||
Choose your preferred installation method:
|
||||
|
||||
@@ -57,10 +57,24 @@ uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
|
||||
Then use the tool directly:
|
||||
|
||||
```bash
|
||||
# Create new project
|
||||
specify init <PROJECT_NAME>
|
||||
|
||||
# Or initialize in existing project
|
||||
specify init . --ai claude
|
||||
# or
|
||||
specify init --here --ai claude
|
||||
|
||||
# Check installed tools
|
||||
specify check
|
||||
```
|
||||
|
||||
To upgrade Specify, see the [Upgrade Guide](./docs/upgrade.md) for detailed instructions. Quick upgrade:
|
||||
|
||||
```bash
|
||||
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
|
||||
```
|
||||
|
||||
#### Option 2: One-time Usage
|
||||
|
||||
Run directly without installing:
|
||||
@@ -78,6 +92,8 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME
|
||||
|
||||
### 2. Establish project principles
|
||||
|
||||
Launch your AI assistant in the project directory. The `/speckit.*` commands are available in the assistant.
|
||||
|
||||
Use the **`/speckit.constitution`** command to create your project's governing principles and development guidelines that will guide all subsequent development.
|
||||
|
||||
```bash
|
||||
@@ -126,20 +142,26 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
|
||||
|
||||
## 🤖 Supported AI Agents
|
||||
|
||||
| Agent | Support | Notes |
|
||||
|-----------------------------------------------------------|---------|---------------------------------------------------|
|
||||
| [Claude Code](https://www.anthropic.com/claude-code) | ✅ | |
|
||||
| [GitHub Copilot](https://code.visualstudio.com/) | ✅ | |
|
||||
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | ✅ | |
|
||||
| [Cursor](https://cursor.sh/) | ✅ | |
|
||||
| [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | |
|
||||
| [opencode](https://opencode.ai/) | ✅ | |
|
||||
| [Windsurf](https://windsurf.com/) | ✅ | |
|
||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
|
||||
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | ✅ | |
|
||||
| [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. |
|
||||
| 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) | ✅ | |
|
||||
| [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/) | ✅ | |
|
||||
| [IBM Bob](https://www.ibm.com/products/bob) | ✅ | IDE-based agent with slash command support |
|
||||
| [Jules](https://jules.google.com/) | ✅ | |
|
||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
|
||||
| [opencode](https://opencode.ai/) | ✅ | |
|
||||
| [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | |
|
||||
| [Roo Code](https://roocode.com/) | ✅ | |
|
||||
| [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | |
|
||||
| [Windsurf](https://windsurf.com/) | ✅ | |
|
||||
|
||||
## 🔧 Specify CLI Reference
|
||||
|
||||
@@ -147,25 +169,25 @@ The `specify` command supports the following options:
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|-------------|----------------------------------------------------------------|
|
||||
| `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`) |
|
||||
| Command | Description |
|
||||
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `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`) |
|
||||
|
||||
### `specify init` Arguments & 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`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, 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 |
|
||||
| `--here` | Flag | Initialize project in the current directory instead of creating a new one |
|
||||
| `--force` | Flag | Force merge/overwrite when initializing in current directory (skip confirmation) |
|
||||
| `--skip-tls` | Flag | Skip SSL/TLS verification (not recommended) |
|
||||
| `--debug` | Flag | Enable detailed debug output for troubleshooting |
|
||||
| `--github-token` | Option | GitHub token for API requests (or set GH_TOKEN/GITHUB_TOKEN env variable) |
|
||||
| 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`, `amp`, `shai`, `q`, `bob`, or `qoder` |
|
||||
| `--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 |
|
||||
| `--here` | Flag | Initialize project in the current directory instead of creating a new one |
|
||||
| `--force` | Flag | Force merge/overwrite when initializing in current directory (skip confirmation) |
|
||||
| `--skip-tls` | Flag | Skip SSL/TLS verification (not recommended) |
|
||||
| `--debug` | Flag | Enable detailed debug output for troubleshooting |
|
||||
| `--github-token` | Option | GitHub token for API requests (or set GH_TOKEN/GITHUB_TOKEN env variable) |
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -177,11 +199,23 @@ specify init my-project
|
||||
specify init my-project --ai claude
|
||||
|
||||
# Initialize with Cursor support
|
||||
specify init my-project --ai cursor
|
||||
specify init my-project --ai cursor-agent
|
||||
|
||||
# Initialize with Qoder support
|
||||
specify init my-project --ai qoder
|
||||
|
||||
# Initialize with Windsurf support
|
||||
specify init my-project --ai windsurf
|
||||
|
||||
# Initialize with Amp support
|
||||
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)
|
||||
specify init my-project --ai copilot --script ps
|
||||
|
||||
@@ -192,7 +226,7 @@ specify init --here --ai copilot
|
||||
|
||||
# Force merge into current (non-empty) directory without confirmation
|
||||
specify init . --force --ai copilot
|
||||
# or
|
||||
# or
|
||||
specify init --here --force --ai copilot
|
||||
|
||||
# Skip git initialization
|
||||
@@ -216,48 +250,48 @@ After running `specify init`, your AI coding agent will have access to these sla
|
||||
|
||||
Essential commands for the Spec-Driven Development workflow:
|
||||
|
||||
| Command | Description |
|
||||
|--------------------------|-----------------------------------------------------------------------|
|
||||
| `/speckit.constitution` | Create or update project governing principles and development guidelines |
|
||||
| `/speckit.specify` | Define what you want to build (requirements and user stories) |
|
||||
| `/speckit.plan` | Create technical implementation plans with your chosen tech stack |
|
||||
| `/speckit.tasks` | Generate actionable task lists for implementation |
|
||||
| `/speckit.implement` | Execute all tasks to build the feature according to the plan |
|
||||
| Command | Description |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| `/speckit.constitution` | Create or update project governing principles and development guidelines |
|
||||
| `/speckit.specify` | Define what you want to build (requirements and user stories) |
|
||||
| `/speckit.plan` | Create technical implementation plans with your chosen tech stack |
|
||||
| `/speckit.tasks` | Generate actionable task lists for implementation |
|
||||
| `/speckit.implement` | Execute all tasks to build the feature according to the plan |
|
||||
|
||||
#### Optional Commands
|
||||
|
||||
Additional commands for enhanced quality and validation:
|
||||
|
||||
| Command | Description |
|
||||
|----------------------|-----------------------------------------------------------------------|
|
||||
| `/speckit.clarify` | Clarify underspecified areas (recommended before `/speckit.plan`; formerly `/quizme`) |
|
||||
| `/speckit.analyze` | Cross-artifact consistency & coverage analysis (run after `/speckit.tasks`, before `/speckit.implement`) |
|
||||
| Command | Description |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `/speckit.clarify` | Clarify underspecified areas (recommended before `/speckit.plan`; formerly `/quizme`) |
|
||||
| `/speckit.analyze` | Cross-artifact consistency & coverage analysis (run after `/speckit.tasks`, before `/speckit.implement`) |
|
||||
| `/speckit.checklist` | Generate custom quality checklists that validate requirements completeness, clarity, and consistency (like "unit tests for English") |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|------------------|------------------------------------------------------------------------------------------------|
|
||||
| `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches.<br/>**Must be set in the context of the agent you're working with prior to using `/speckit.plan` or follow-up commands. |
|
||||
| Variable | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `SPECIFY_FEATURE` | Override feature detection for non-Git repositories. Set to the feature directory name (e.g., `001-photo-albums`) to work on a specific feature when not using Git branches.<br/>\*\*Must be set in the context of the agent you're working with prior to using `/speckit.plan` or follow-up commands. |
|
||||
|
||||
## 📚 Core philosophy
|
||||
## 📚 Core Philosophy
|
||||
|
||||
Spec-Driven Development is a structured process that emphasizes:
|
||||
|
||||
- **Intent-driven development** where specifications define the "_what_" before the "_how_"
|
||||
- **Intent-driven development** where specifications define the "*what*" before the "*how*"
|
||||
- **Rich specification creation** using guardrails and organizational principles
|
||||
- **Multi-step refinement** rather than one-shot code generation from prompts
|
||||
- **Heavy reliance** on advanced AI model capabilities for specification interpretation
|
||||
|
||||
## 🌟 Development phases
|
||||
## 🌟 Development Phases
|
||||
|
||||
| Phase | Focus | Key Activities |
|
||||
|-------|-------|----------------|
|
||||
| **0-to-1 Development** ("Greenfield") | Generate from scratch | <ul><li>Start with high-level requirements</li><li>Generate specifications</li><li>Plan implementation steps</li><li>Build production-ready applications</li></ul> |
|
||||
| **Creative Exploration** | Parallel implementations | <ul><li>Explore diverse solutions</li><li>Support multiple technology stacks & architectures</li><li>Experiment with UX patterns</li></ul> |
|
||||
| **Iterative Enhancement** ("Brownfield") | Brownfield modernization | <ul><li>Add features iteratively</li><li>Modernize legacy systems</li><li>Adapt processes</li></ul> |
|
||||
| Phase | Focus | Key Activities |
|
||||
| ---------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **0-to-1 Development** ("Greenfield") | Generate from scratch | <ul><li>Start with high-level requirements</li><li>Generate specifications</li><li>Plan implementation steps</li><li>Build production-ready applications</li></ul> |
|
||||
| **Creative Exploration** | Parallel implementations | <ul><li>Explore diverse solutions</li><li>Support multiple technology stacks & architectures</li><li>Experiment with UX patterns</li></ul> |
|
||||
| **Iterative Enhancement** ("Brownfield") | Brownfield modernization | <ul><li>Add features iteratively</li><li>Modernize legacy systems</li><li>Adapt processes</li></ul> |
|
||||
|
||||
## 🎯 Experimental goals
|
||||
## 🎯 Experimental Goals
|
||||
|
||||
Our research and experimentation focus on:
|
||||
|
||||
@@ -285,22 +319,22 @@ Our research and experimentation focus on:
|
||||
|
||||
## 🔧 Prerequisites
|
||||
|
||||
- **Linux/macOS** (or WSL2 on Windows)
|
||||
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Cursor](https://cursor.sh/), [Qwen CLI](https://github.com/QwenLM/qwen-code), [opencode](https://opencode.ai/), [Codex CLI](https://github.com/openai/codex), [Windsurf](https://windsurf.com/), or [Amazon Q Developer CLI](https://aws.amazon.com/developer/learning/q-developer-cli/)
|
||||
- **Linux/macOS/Windows**
|
||||
- [Supported](#-supported-ai-agents) AI coding agent.
|
||||
- [uv](https://docs.astral.sh/uv/) for package management
|
||||
- [Python 3.11+](https://www.python.org/downloads/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
If you encounter issues with an agent, please open an issue so we can refine the integration.
|
||||
|
||||
## 📖 Learn more
|
||||
## 📖 Learn More
|
||||
|
||||
- **[Complete Spec-Driven Development Methodology](./spec-driven.md)** - Deep dive into the full process
|
||||
- **[Detailed Walkthrough](#-detailed-process)** - Step-by-step implementation guide
|
||||
|
||||
---
|
||||
|
||||
## 📋 Detailed process
|
||||
## 📋 Detailed Process
|
||||
|
||||
<details>
|
||||
<summary>Click to expand the detailed step-by-step walkthrough</summary>
|
||||
@@ -347,7 +381,7 @@ specify init . --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, 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, 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:
|
||||
|
||||
```bash
|
||||
specify init <project_name> --ai claude --ignore-agent-tools
|
||||
@@ -373,8 +407,8 @@ This step creates or updates the `.specify/memory/constitution.md` file with you
|
||||
|
||||
With your project principles established, you can now create the functional specifications. Use the `/speckit.specify` command and then provide the concrete requirements for the project you want to develop.
|
||||
|
||||
>[!IMPORTANT]
|
||||
>Be as explicit as possible about _what_ you are trying to build and _why_. **Do not focus on the tech stack at this point**.
|
||||
> [!IMPORTANT]
|
||||
> Be as explicit as possible about *what* you are trying to build and *why*. **Do not focus on the tech stack at this point**.
|
||||
|
||||
An example prompt:
|
||||
|
||||
@@ -408,16 +442,16 @@ At this stage, your project folder contents should resemble the following:
|
||||
```text
|
||||
└── .specify
|
||||
├── memory
|
||||
│ └── constitution.md
|
||||
│ └── constitution.md
|
||||
├── scripts
|
||||
│ ├── check-prerequisites.sh
|
||||
│ ├── common.sh
|
||||
│ ├── create-new-feature.sh
|
||||
│ ├── setup-plan.sh
|
||||
│ └── update-claude-md.sh
|
||||
│ ├── check-prerequisites.sh
|
||||
│ ├── common.sh
|
||||
│ ├── create-new-feature.sh
|
||||
│ ├── setup-plan.sh
|
||||
│ └── update-claude-md.sh
|
||||
├── specs
|
||||
│ └── 001-create-taskify
|
||||
│ └── spec.md
|
||||
│ └── 001-create-taskify
|
||||
│ └── spec.md
|
||||
└── templates
|
||||
├── plan-template.md
|
||||
├── spec-template.md
|
||||
@@ -431,6 +465,7 @@ With the baseline specification created, you can go ahead and clarify any of the
|
||||
You should run the structured clarification workflow **before** creating a technical plan to reduce rework downstream.
|
||||
|
||||
Preferred order:
|
||||
|
||||
1. Use `/speckit.clarify` (structured) – sequential, coverage-based questioning that records answers in a Clarifications section.
|
||||
2. Optionally follow up with ad-hoc free-form refinement if something still feels vague.
|
||||
|
||||
@@ -468,23 +503,23 @@ The output of this step will include a number of implementation detail documents
|
||||
.
|
||||
├── CLAUDE.md
|
||||
├── memory
|
||||
│ └── constitution.md
|
||||
│ └── constitution.md
|
||||
├── scripts
|
||||
│ ├── check-prerequisites.sh
|
||||
│ ├── common.sh
|
||||
│ ├── create-new-feature.sh
|
||||
│ ├── setup-plan.sh
|
||||
│ └── update-claude-md.sh
|
||||
│ ├── check-prerequisites.sh
|
||||
│ ├── common.sh
|
||||
│ ├── create-new-feature.sh
|
||||
│ ├── setup-plan.sh
|
||||
│ └── update-claude-md.sh
|
||||
├── specs
|
||||
│ └── 001-create-taskify
|
||||
│ ├── contracts
|
||||
│ │ ├── api-spec.json
|
||||
│ │ └── signalr-spec.md
|
||||
│ ├── data-model.md
|
||||
│ ├── plan.md
|
||||
│ ├── quickstart.md
|
||||
│ ├── research.md
|
||||
│ └── spec.md
|
||||
│ └── 001-create-taskify
|
||||
│ ├── contracts
|
||||
│ │ ├── api-spec.json
|
||||
│ │ └── signalr-spec.md
|
||||
│ ├── data-model.md
|
||||
│ ├── plan.md
|
||||
│ ├── quickstart.md
|
||||
│ ├── research.md
|
||||
│ └── spec.md
|
||||
└── templates
|
||||
├── CLAUDE-template.md
|
||||
├── plan-template.md
|
||||
@@ -516,8 +551,8 @@ researching .NET Aspire in general and I don't think that's gonna do much for us
|
||||
That's way too untargeted research. The research needs to help you solve a specific targeted question.
|
||||
```
|
||||
|
||||
>[!NOTE]
|
||||
>Claude Code might be over-eager and add components that you did not ask for. Ask it to clarify the rationale and the source of the change.
|
||||
> [!NOTE]
|
||||
> Claude Code might be over-eager and add components that you did not ask for. Ask it to clarify the rationale and the source of the change.
|
||||
|
||||
### **STEP 5:** Have Claude Code validate the plan
|
||||
|
||||
@@ -535,10 +570,29 @@ This helps refine the implementation plan and helps you avoid potential blind sp
|
||||
|
||||
You can also ask Claude Code (if you have the [GitHub CLI](https://docs.github.com/en/github-cli/github-cli) installed) to go ahead and create a pull request from your current branch to `main` with a detailed description, to make sure that the effort is properly tracked.
|
||||
|
||||
>[!NOTE]
|
||||
>Before you have the agent implement it, it's also worth prompting Claude Code to cross-check the details to see if there are any over-engineered pieces (remember - it can be over-eager). If over-engineered components or decisions exist, you can ask Claude Code to resolve them. Ensure that Claude Code follows the [constitution](base/memory/constitution.md) as the foundational piece that it must adhere to when establishing the plan.
|
||||
> [!NOTE]
|
||||
> Before you have the agent implement it, it's also worth prompting Claude Code to cross-check the details to see if there are any over-engineered pieces (remember - it can be over-eager). If over-engineered components or decisions exist, you can ask Claude Code to resolve them. Ensure that Claude Code follows the [constitution](base/memory/constitution.md) as the foundational piece that it must adhere to when establishing the plan.
|
||||
|
||||
### STEP 6: Implementation
|
||||
### **STEP 6:** Generate task breakdown with /speckit.tasks
|
||||
|
||||
With the implementation plan validated, you can now break down the plan into specific, actionable tasks that can be executed in the correct order. Use the `/speckit.tasks` command to automatically generate a detailed task breakdown from your implementation plan:
|
||||
|
||||
```text
|
||||
/speckit.tasks
|
||||
```
|
||||
|
||||
This step creates a `tasks.md` file in your feature specification directory that contains:
|
||||
|
||||
- **Task breakdown organized by user story** - Each user story becomes a separate implementation phase with its own set of tasks
|
||||
- **Dependency management** - Tasks are ordered to respect dependencies between components (e.g., models before services, services before endpoints)
|
||||
- **Parallel execution markers** - Tasks that can run in parallel are marked with `[P]` to optimize development workflow
|
||||
- **File path specifications** - Each task includes the exact file paths where implementation should occur
|
||||
- **Test-driven development structure** - If tests are requested, test tasks are included and ordered to be written before implementation
|
||||
- **Checkpoint validation** - Each user story phase includes checkpoints to validate independent functionality
|
||||
|
||||
The generated tasks.md provides a clear roadmap for the `/speckit.implement` command, ensuring systematic implementation that maintains code quality and allows for incremental delivery of user stories.
|
||||
|
||||
### **STEP 7:** Implementation
|
||||
|
||||
Once ready, use the `/speckit.implement` command to execute your implementation plan:
|
||||
|
||||
@@ -547,14 +601,15 @@ Once ready, use the `/speckit.implement` command to execute your implementation
|
||||
```
|
||||
|
||||
The `/speckit.implement` command will:
|
||||
|
||||
- Validate that all prerequisites are in place (constitution, spec, plan, and tasks)
|
||||
- Parse the task breakdown from `tasks.md`
|
||||
- Execute tasks in the correct order, respecting dependencies and parallel execution markers
|
||||
- Follow the TDD approach defined in your task plan
|
||||
- Provide progress updates and handle errors appropriately
|
||||
|
||||
>[!IMPORTANT]
|
||||
>The AI agent will execute local CLI commands (such as `dotnet`, `npm`, etc.) - make sure you have the required tools installed on your machine.
|
||||
> [!IMPORTANT]
|
||||
> The AI agent will execute local CLI commands (such as `dotnet`, `npm`, etc.) - make sure you have the required tools installed on your machine.
|
||||
|
||||
Once the implementation is complete, test the application and resolve any runtime errors that may not be visible in CLI logs (e.g., browser console errors). You can copy and paste such errors back to your AI agent for resolution.
|
||||
|
||||
|
||||
22
SECURITY.md
22
SECURITY.md
@@ -1,10 +1,10 @@
|
||||
Thanks for helping make GitHub safe for everyone.
|
||||
# Security Policy
|
||||
|
||||
# Security
|
||||
Thanks for helping make GitHub safe for everyone.
|
||||
|
||||
GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub).
|
||||
|
||||
Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation.
|
||||
Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
@@ -16,16 +16,16 @@ Instead, please send an email to opensource-security[@]github.com.
|
||||
|
||||
Please include as much of the information listed below as you can to help us better understand and resolve the issue:
|
||||
|
||||
* The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
- The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)
|
||||
- Full paths of source file(s) related to the manifestation of the issue
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- Step-by-step instructions to reproduce the issue
|
||||
- Proof-of-concept or exploit code (if possible)
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
## Policy
|
||||
|
||||
See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
|
||||
See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Support
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
|
||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -6,3 +6,4 @@ obj/
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.log
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ This folder contains the documentation source files for Spec Kit, built using [D
|
||||
To build the documentation locally:
|
||||
|
||||
1. Install DocFX:
|
||||
|
||||
```bash
|
||||
dotnet tool install -g docfx
|
||||
```
|
||||
|
||||
2. Build the documentation:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
docfx docfx.json --serve
|
||||
|
||||
@@ -68,3 +68,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,13 +12,14 @@ Spec-Driven Development **flips the script** on traditional software development
|
||||
|
||||
- [Installation Guide](installation.md)
|
||||
- [Quick Start Guide](quickstart.md)
|
||||
- [Upgrade Guide](upgrade.md)
|
||||
- [Local Development](local-development.md)
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Spec-Driven Development is a structured process that emphasizes:
|
||||
|
||||
- **Intent-driven development** where specifications define the "_what_" before the "_how_"
|
||||
- **Intent-driven development** where specifications define the "*what*" before the "*how*"
|
||||
- **Rich specification creation** using guardrails and organizational principles
|
||||
- **Multi-step refinement** rather than one-shot code generation from prompts
|
||||
- **Heavy reliance** on advanced AI model capabilities for specification interpretation
|
||||
@@ -36,19 +37,23 @@ Spec-Driven Development is a structured process that emphasizes:
|
||||
Our research and experimentation focus on:
|
||||
|
||||
### Technology Independence
|
||||
|
||||
- Create applications using diverse technology stacks
|
||||
- Validate the hypothesis that Spec-Driven Development is a process not tied to specific technologies, programming languages, or frameworks
|
||||
|
||||
### Enterprise Constraints
|
||||
|
||||
- Demonstrate mission-critical application development
|
||||
- Incorporate organizational constraints (cloud providers, tech stacks, engineering practices)
|
||||
- Support enterprise design systems and compliance requirements
|
||||
|
||||
### User-Centric Development
|
||||
|
||||
- Build applications for different user cohorts and preferences
|
||||
- Support various development approaches (from vibe-coding to AI-native development)
|
||||
|
||||
### Creative & Iterative Processes
|
||||
|
||||
- Validate the concept of parallel implementation exploration
|
||||
- Provide robust iterative feature development workflows
|
||||
- Extend processes to handle upgrades and modernization tasks
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Prerequisites
|
||||
|
||||
- **Linux/macOS** (or Windows; PowerShell scripts now supported without WSL)
|
||||
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), or [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
||||
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Codebuddy CLI](https://www.codebuddy.ai/cli) or [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
||||
- [uv](https://docs.astral.sh/uv/) for package management
|
||||
- [Python 3.11+](https://www.python.org/downloads/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
@@ -34,6 +34,7 @@ You can proactively specify your AI agent during initialization:
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai claude
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai gemini
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai copilot
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --ai codebuddy
|
||||
```
|
||||
|
||||
### Specify Script Type (Shell vs PowerShell)
|
||||
@@ -41,11 +42,13 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <project_name
|
||||
All automation scripts now have both Bash (`.sh`) and PowerShell (`.ps1`) variants.
|
||||
|
||||
Auto behavior:
|
||||
|
||||
- Windows default: `ps`
|
||||
- Other OS default: `sh`
|
||||
- Interactive mode: you'll be prompted unless you pass `--script`
|
||||
|
||||
Force a specific script type:
|
||||
|
||||
```bash
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --script sh
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <project_name> --script ps
|
||||
@@ -62,6 +65,7 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <project_name
|
||||
## Verification
|
||||
|
||||
After initialization, you should see the following commands available in your AI agent:
|
||||
|
||||
- `/speckit.specify` - Create specifications
|
||||
- `/speckit.plan` - Generate implementation plans
|
||||
- `/speckit.tasks` - Break down into actionable tasks
|
||||
|
||||
@@ -73,12 +73,14 @@ uvx --from /mnt/c/GitHub/spec-kit specify init demo-anywhere --ai copilot --igno
|
||||
```
|
||||
|
||||
Set an environment variable for convenience:
|
||||
|
||||
```bash
|
||||
export SPEC_KIT_SRC=/mnt/c/GitHub/spec-kit
|
||||
uvx --from "$SPEC_KIT_SRC" specify init demo-env --ai copilot --ignore-agent-tools --script ps
|
||||
```
|
||||
|
||||
(Optional) Define a shell function:
|
||||
|
||||
```bash
|
||||
specify-dev() { uvx --from /mnt/c/GitHub/spec-kit specify "$@"; }
|
||||
# Then
|
||||
@@ -93,11 +95,13 @@ After running an `init`, check that shell scripts are executable on POSIX system
|
||||
ls -l scripts | grep .sh
|
||||
# Expect owner execute bit (e.g. -rwxr-xr-x)
|
||||
```
|
||||
|
||||
On Windows you will instead use the `.ps1` scripts (no chmod needed).
|
||||
|
||||
## 6. Run Lint / Basic Checks (Add Your Own)
|
||||
|
||||
Currently no enforced lint config is bundled, but you can quickly sanity check importability:
|
||||
|
||||
```bash
|
||||
python -c "import specify_cli; print('Import OK')"
|
||||
```
|
||||
@@ -110,6 +114,7 @@ Validate packaging before publishing:
|
||||
uv build
|
||||
ls dist/
|
||||
```
|
||||
|
||||
Install the built artifact into a fresh throwaway environment if needed.
|
||||
|
||||
## 8. Using a Temporary Workspace
|
||||
@@ -120,6 +125,7 @@ When testing `init --here` in a dirty directory, create a temp workspace:
|
||||
mkdir /tmp/spec-test && cd /tmp/spec-test
|
||||
python -m src.specify_cli init --here --ai claude --ignore-agent-tools --script sh # if repo copied here
|
||||
```
|
||||
|
||||
Or copy only the modified CLI portion if you want a lighter sandbox.
|
||||
|
||||
## 9. Debug Network / TLS Skips
|
||||
@@ -130,6 +136,7 @@ If you need to bypass TLS validation while experimenting:
|
||||
specify check --skip-tls
|
||||
specify init demo --skip-tls --ai gemini --ignore-agent-tools --script ps
|
||||
```
|
||||
|
||||
(Use only for local experimentation.)
|
||||
|
||||
## 10. Rapid Edit Loop Summary
|
||||
@@ -146,6 +153,7 @@ specify init demo --skip-tls --ai gemini --ignore-agent-tools --script ps
|
||||
## 11. Cleaning Up
|
||||
|
||||
Remove build artifacts / virtual env quickly:
|
||||
|
||||
```bash
|
||||
rm -rf .venv dist build *.egg-info
|
||||
```
|
||||
@@ -165,4 +173,3 @@ rm -rf .venv dist build *.egg-info
|
||||
- Update docs and run through Quick Start using your modified CLI
|
||||
- Open a PR when satisfied
|
||||
- (Optional) Tag a release once changes land in `main`
|
||||
|
||||
|
||||
@@ -2,49 +2,98 @@
|
||||
|
||||
This guide will help you get started with Spec-Driven Development using Spec Kit.
|
||||
|
||||
> 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`.
|
||||
> [!NOTE]
|
||||
> 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 4-Step Process
|
||||
## The 6-Step Process
|
||||
|
||||
### 1. Install Specify
|
||||
> [!TIP]
|
||||
> **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.
|
||||
|
||||
Initialize your project depending on the coding agent you're using:
|
||||
### Step 1: Install Specify
|
||||
|
||||
**In your terminal**, run the `specify` CLI command to initialize your project:
|
||||
|
||||
```bash
|
||||
# Create a new project directory
|
||||
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):
|
||||
|
||||
```bash
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script ps # Force PowerShell
|
||||
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script sh # Force POSIX shell
|
||||
```
|
||||
|
||||
### 2. Create the Spec
|
||||
### Step 2: Define Your Constitution
|
||||
|
||||
Use the `/speckit.specify` command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.
|
||||
**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.
|
||||
|
||||
```bash
|
||||
```markdown
|
||||
/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.
|
||||
```
|
||||
|
||||
### 3. Create a Technical Implementation Plan
|
||||
### Step 4: Refine the Spec
|
||||
|
||||
Use the `/speckit.plan` command to provide your tech stack and architecture choices.
|
||||
**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.
|
||||
|
||||
```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.
|
||||
```
|
||||
|
||||
### 4. Break Down and Implement
|
||||
### Step 6: Break Down and Implement
|
||||
|
||||
Use `/speckit.tasks` to create an actionable task list, then ask your agent to implement the feature.
|
||||
**In the chat**, use the `/speckit.tasks` slash command to create an actionable task list.
|
||||
|
||||
```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
|
||||
|
||||
Here's a complete example of building a team productivity platform:
|
||||
|
||||
### Step 1: Define Requirements with `/speckit.specify`
|
||||
### Step 1: Define Constitution
|
||||
|
||||
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
|
||||
Develop Taskify, a team productivity platform. It should allow users to create projects, add team members,
|
||||
@@ -53,58 +102,51 @@ 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
|
||||
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
|
||||
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.
|
||||
first testing thing to ensure that our basic features are set up.
|
||||
```
|
||||
|
||||
### Step 2: Refine the Specification
|
||||
### Step 3: Refine the Specification
|
||||
|
||||
After the initial specification is created, clarify any missing requirements:
|
||||
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.
|
||||
|
||||
```text
|
||||
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.
|
||||
```bash
|
||||
/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.
|
||||
```
|
||||
|
||||
Also validate the specification checklist:
|
||||
You can continue to refine the spec with more details using `/speckit.clarify`:
|
||||
|
||||
```text
|
||||
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.
|
||||
```bash
|
||||
/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.
|
||||
```
|
||||
|
||||
### Step 3: Generate Technical Plan with `/speckit.plan`
|
||||
### Step 4: Validate the Spec
|
||||
|
||||
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:
|
||||
|
||||
```text
|
||||
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.
|
||||
```bash
|
||||
/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.
|
||||
```
|
||||
|
||||
### Step 4: Validate and Implement
|
||||
### Step 6: Validate and Implement
|
||||
|
||||
Have your AI agent audit the implementation plan:
|
||||
Have your AI agent audit the implementation plan using `/speckit.analyze`:
|
||||
|
||||
```text
|
||||
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.
|
||||
```bash
|
||||
/speckit.analyze
|
||||
```
|
||||
|
||||
Finally, implement the solution:
|
||||
|
||||
```text
|
||||
implement specs/002-create-taskify/plan.md
|
||||
```bash
|
||||
/speckit.implement
|
||||
```
|
||||
|
||||
## Key Principles
|
||||
@@ -117,6 +159,6 @@ implement specs/002-create-taskify/plan.md
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the complete methodology for in-depth guidance
|
||||
- Check out more examples in the repository
|
||||
- Explore the source code on GitHub
|
||||
- Read the [complete methodology](../spec-driven.md) for in-depth guidance
|
||||
- Check out [more examples](../templates) in the repository
|
||||
- Explore the [source code on GitHub](https://github.com/github/spec-kit)
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
href: installation.md
|
||||
- name: Quick Start
|
||||
href: quickstart.md
|
||||
- name: Upgrade
|
||||
href: upgrade.md
|
||||
|
||||
# Development workflows
|
||||
- name: Development
|
||||
|
||||
444
docs/upgrade.md
Normal file
444
docs/upgrade.md
Normal file
@@ -0,0 +1,444 @@
|
||||
# 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:
|
||||
|
||||
```text
|
||||
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:**
|
||||
|
||||
```text
|
||||
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
|
||||
@@ -47,4 +47,4 @@
|
||||
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
|
||||
|
||||
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
||||
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
||||
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "specify-cli"
|
||||
version = "0.0.18"
|
||||
version = "0.0.22"
|
||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
@@ -21,3 +21,4 @@ build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/specify_cli"]
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ EOF
|
||||
done
|
||||
|
||||
# Source common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
# Get feature paths and validate branch
|
||||
@@ -163,4 +163,4 @@ else
|
||||
if $INCLUDE_TASKS; then
|
||||
check_file "$TASKS" "tasks.md"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -7,7 +7,7 @@ get_repo_root() {
|
||||
git rev-parse --show-toplevel
|
||||
else
|
||||
# Fall back to script location for non-git repos
|
||||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
(cd "$script_dir/../../.." && pwd)
|
||||
fi
|
||||
}
|
||||
@@ -19,21 +19,21 @@ get_current_branch() {
|
||||
echo "$SPECIFY_FEATURE"
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
# Then check git if available
|
||||
if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
# For non-git repos, try to find the latest feature directory
|
||||
local repo_root=$(get_repo_root)
|
||||
local specs_dir="$repo_root/specs"
|
||||
|
||||
|
||||
if [[ -d "$specs_dir" ]]; then
|
||||
local latest_feature=""
|
||||
local highest=0
|
||||
|
||||
|
||||
for dir in "$specs_dir"/*; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
local dirname=$(basename "$dir")
|
||||
@@ -47,13 +47,13 @@ get_current_branch() {
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [[ -n "$latest_feature" ]]; then
|
||||
echo "$latest_feature"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
echo "main" # Final fallback
|
||||
}
|
||||
|
||||
@@ -65,35 +65,77 @@ has_git() {
|
||||
check_feature_branch() {
|
||||
local branch="$1"
|
||||
local has_git_repo="$2"
|
||||
|
||||
|
||||
# For non-git repos, we can't enforce branch naming but still provide output
|
||||
if [[ "$has_git_repo" != "true" ]]; then
|
||||
echo "[specify] Warning: Git repository not detected; skipped branch validation" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! "$branch" =~ ^[0-9]{3}- ]]; then
|
||||
echo "ERROR: Not on a feature branch. Current branch: $branch" >&2
|
||||
echo "Feature branches should be named like: 001-feature-name" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_feature_dir() { echo "$1/specs/$2"; }
|
||||
|
||||
# Find feature directory by numeric prefix instead of exact branch match
|
||||
# This allows multiple branches to work on the same spec (e.g., 004-fix-bug, 004-add-feature)
|
||||
find_feature_dir_by_prefix() {
|
||||
local repo_root="$1"
|
||||
local branch_name="$2"
|
||||
local specs_dir="$repo_root/specs"
|
||||
|
||||
# Extract numeric prefix from branch (e.g., "004" from "004-whatever")
|
||||
if [[ ! "$branch_name" =~ ^([0-9]{3})- ]]; then
|
||||
# If branch doesn't have numeric prefix, fall back to exact match
|
||||
echo "$specs_dir/$branch_name"
|
||||
return
|
||||
fi
|
||||
|
||||
local prefix="${BASH_REMATCH[1]}"
|
||||
|
||||
# Search for directories in specs/ that start with this prefix
|
||||
local matches=()
|
||||
if [[ -d "$specs_dir" ]]; then
|
||||
for dir in "$specs_dir"/"$prefix"-*; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
matches+=("$(basename "$dir")")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Handle results
|
||||
if [[ ${#matches[@]} -eq 0 ]]; then
|
||||
# No match found - return the branch name path (will fail later with clear error)
|
||||
echo "$specs_dir/$branch_name"
|
||||
elif [[ ${#matches[@]} -eq 1 ]]; then
|
||||
# Exactly one match - perfect!
|
||||
echo "$specs_dir/${matches[0]}"
|
||||
else
|
||||
# Multiple matches - this shouldn't happen with proper naming convention
|
||||
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
|
||||
echo "Please ensure only one spec directory exists per numeric prefix." >&2
|
||||
echo "$specs_dir/$branch_name" # Return something to avoid breaking the script
|
||||
fi
|
||||
}
|
||||
|
||||
get_feature_paths() {
|
||||
local repo_root=$(get_repo_root)
|
||||
local current_branch=$(get_current_branch)
|
||||
local has_git_repo="false"
|
||||
|
||||
|
||||
if has_git; then
|
||||
has_git_repo="true"
|
||||
fi
|
||||
|
||||
local feature_dir=$(get_feature_dir "$repo_root" "$current_branch")
|
||||
|
||||
|
||||
# Use prefix-based lookup to support multiple branches per spec
|
||||
local feature_dir=$(find_feature_dir_by_prefix "$repo_root" "$current_branch")
|
||||
|
||||
cat <<EOF
|
||||
REPO_ROOT='$repo_root'
|
||||
CURRENT_BRANCH='$current_branch'
|
||||
@@ -111,3 +153,4 @@ EOF
|
||||
|
||||
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||
|
||||
|
||||
@@ -3,18 +3,67 @@
|
||||
set -e
|
||||
|
||||
JSON_MODE=false
|
||||
SHORT_NAME=""
|
||||
BRANCH_NUMBER=""
|
||||
ARGS=()
|
||||
for arg in "$@"; do
|
||||
i=1
|
||||
while [ $i -le $# ]; do
|
||||
arg="${!i}"
|
||||
case "$arg" in
|
||||
--json) JSON_MODE=true ;;
|
||||
--help|-h) echo "Usage: $0 [--json] <feature_description>"; exit 0 ;;
|
||||
*) ARGS+=("$arg") ;;
|
||||
--json)
|
||||
JSON_MODE=true
|
||||
;;
|
||||
--short-name)
|
||||
if [ $((i + 1)) -gt $# ]; then
|
||||
echo 'Error: --short-name requires a value' >&2
|
||||
exit 1
|
||||
fi
|
||||
i=$((i + 1))
|
||||
next_arg="${!i}"
|
||||
# Check if the next argument is another option (starts with --)
|
||||
if [[ "$next_arg" == --* ]]; then
|
||||
echo 'Error: --short-name requires a value' >&2
|
||||
exit 1
|
||||
fi
|
||||
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)
|
||||
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --json Output in JSON format"
|
||||
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 ""
|
||||
echo "Examples:"
|
||||
echo " $0 'Add user authentication system' --short-name 'user-auth'"
|
||||
echo " $0 'Implement OAuth2 integration for API' --number 5"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
ARGS+=("$arg")
|
||||
;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
FEATURE_DESCRIPTION="${ARGS[*]}"
|
||||
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
echo "Usage: $0 [--json] <feature_description>" >&2
|
||||
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -31,10 +80,85 @@ find_repo_root() {
|
||||
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 specs_dir="$1"
|
||||
|
||||
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
|
||||
git fetch --all --prune 2>/dev/null || true
|
||||
|
||||
# Get highest number from ALL branches (not just matching short name)
|
||||
local highest_branch=$(get_highest_from_branches)
|
||||
|
||||
# Get highest number from ALL specs (not just matching short name)
|
||||
local highest_spec=$(get_highest_from_specs "$specs_dir")
|
||||
|
||||
# Take the maximum of both
|
||||
local max_num=$highest_branch
|
||||
if [ "$highest_spec" -gt "$max_num" ]; then
|
||||
max_num=$highest_spec
|
||||
fi
|
||||
|
||||
# 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
|
||||
# to searching for repository markers so the workflow still functions in repositories that
|
||||
# were initialised with --no-git.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
@@ -53,23 +177,99 @@ cd "$REPO_ROOT"
|
||||
SPECS_DIR="$REPO_ROOT/specs"
|
||||
mkdir -p "$SPECS_DIR"
|
||||
|
||||
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
|
||||
# Function to generate branch name with stop word filtering and length filtering
|
||||
generate_branch_name() {
|
||||
local description="$1"
|
||||
|
||||
# Common stop words to filter out
|
||||
local stop_words="^(i|a|an|the|to|for|of|in|on|at|by|with|from|is|are|was|were|be|been|being|have|has|had|do|does|did|will|would|should|could|can|may|might|must|shall|this|that|these|those|my|your|our|their|want|need|add|get|set)$"
|
||||
|
||||
# Convert to lowercase and split into words
|
||||
local clean_name=$(echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/ /g')
|
||||
|
||||
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
|
||||
local meaningful_words=()
|
||||
for word in $clean_name; do
|
||||
# Skip empty words
|
||||
[ -z "$word" ] && continue
|
||||
|
||||
# Keep words that are NOT stop words AND (length >= 3 OR are potential acronyms)
|
||||
if ! echo "$word" | grep -qiE "$stop_words"; then
|
||||
if [ ${#word} -ge 3 ]; then
|
||||
meaningful_words+=("$word")
|
||||
elif echo "$description" | grep -q "\b${word^^}\b"; then
|
||||
# Keep short words if they appear as uppercase in original (likely acronyms)
|
||||
meaningful_words+=("$word")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# If we have meaningful words, use first 3-4 of them
|
||||
if [ ${#meaningful_words[@]} -gt 0 ]; then
|
||||
local max_words=3
|
||||
if [ ${#meaningful_words[@]} -eq 4 ]; then max_words=4; fi
|
||||
|
||||
local result=""
|
||||
local count=0
|
||||
for word in "${meaningful_words[@]}"; do
|
||||
if [ $count -ge $max_words ]; then break; fi
|
||||
if [ -n "$result" ]; then result="$result-"; fi
|
||||
result="$result$word"
|
||||
count=$((count + 1))
|
||||
done
|
||||
echo "$result"
|
||||
else
|
||||
# Fallback to original logic if no meaningful words found
|
||||
local cleaned=$(clean_branch_name "$description")
|
||||
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
|
||||
fi
|
||||
}
|
||||
|
||||
# Generate branch name
|
||||
if [ -n "$SHORT_NAME" ]; then
|
||||
# Use provided short name, just clean it up
|
||||
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
|
||||
else
|
||||
# Generate from description with smart filtering
|
||||
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
|
||||
fi
|
||||
|
||||
NEXT=$((HIGHEST + 1))
|
||||
FEATURE_NUM=$(printf "%03d" "$NEXT")
|
||||
# Determine branch number
|
||||
if [ -z "$BRANCH_NUMBER" ]; then
|
||||
if [ "$HAS_GIT" = true ]; then
|
||||
# Check existing branches on remotes
|
||||
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR")
|
||||
else
|
||||
# Fall back to local directory check
|
||||
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
|
||||
BRANCH_NUMBER=$((HIGHEST + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
BRANCH_NAME=$(echo "$FEATURE_DESCRIPTION" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
|
||||
WORDS=$(echo "$BRANCH_NAME" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//')
|
||||
BRANCH_NAME="${FEATURE_NUM}-${WORDS}"
|
||||
# Force base-10 interpretation to prevent octal conversion (e.g., 010 → 8 in octal, but should be 10 in decimal)
|
||||
FEATURE_NUM=$(printf "%03d" "$((10#$BRANCH_NUMBER))")
|
||||
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
||||
|
||||
# GitHub enforces a 244-byte limit on branch names
|
||||
# Validate and truncate if necessary
|
||||
MAX_BRANCH_LENGTH=244
|
||||
if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
|
||||
# Calculate how much we need to trim from suffix
|
||||
# Account for: feature number (3) + hyphen (1) = 4 chars
|
||||
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - 4))
|
||||
|
||||
# Truncate suffix at word boundary if possible
|
||||
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
|
||||
# Remove trailing hyphen if truncation created one
|
||||
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
|
||||
|
||||
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
|
||||
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
|
||||
|
||||
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
|
||||
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
|
||||
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
|
||||
fi
|
||||
|
||||
if [ "$HAS_GIT" = true ]; then
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
|
||||
@@ -24,7 +24,7 @@ for arg in "$@"; do
|
||||
done
|
||||
|
||||
# Get script directory and load common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
# Get all paths and variables from common functions
|
||||
@@ -58,3 +58,4 @@ else
|
||||
echo "BRANCH: $CURRENT_BRANCH"
|
||||
echo "HAS_GIT: $HAS_GIT"
|
||||
fi
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
#
|
||||
# 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, Qoder CLI, Amp, SHAI, or Amazon Q Developer CLI
|
||||
# - Can update single agents or all existing agent files
|
||||
# - Creates default Claude file if no agent files exist
|
||||
#
|
||||
# Usage: ./update-agent-context.sh [agent_type]
|
||||
# Agent types: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|q
|
||||
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|shai|q|bob|qoder
|
||||
# Leave empty to update all existing agent files
|
||||
|
||||
set -e
|
||||
@@ -49,7 +49,7 @@ set -o pipefail
|
||||
#==============================================================================
|
||||
|
||||
# Get script directory and load common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
# Get all paths and variables from common functions
|
||||
@@ -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/agents/copilot-instructions.md"
|
||||
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
||||
QWEN_FILE="$REPO_ROOT/QWEN.md"
|
||||
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
|
||||
@@ -69,7 +69,12 @@ WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md"
|
||||
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"
|
||||
QODER_FILE="$REPO_ROOT/QODER.md"
|
||||
AMP_FILE="$REPO_ROOT/AGENTS.md"
|
||||
SHAI_FILE="$REPO_ROOT/SHAI.md"
|
||||
Q_FILE="$REPO_ROOT/AGENTS.md"
|
||||
BOB_FILE="$REPO_ROOT/AGENTS.md"
|
||||
|
||||
# Template file
|
||||
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
|
||||
@@ -249,7 +254,7 @@ get_commands_for_language() {
|
||||
echo "cargo test && cargo clippy"
|
||||
;;
|
||||
*"JavaScript"*|*"TypeScript"*)
|
||||
echo "npm test && npm run lint"
|
||||
echo "npm test \\&\\& npm run lint"
|
||||
;;
|
||||
*)
|
||||
echo "# Add commands for $lang"
|
||||
@@ -389,12 +394,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
|
||||
@@ -455,6 +473,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
|
||||
@@ -557,7 +591,7 @@ update_specific_agent() {
|
||||
copilot)
|
||||
update_agent_file "$COPILOT_FILE" "GitHub Copilot"
|
||||
;;
|
||||
cursor)
|
||||
cursor-agent)
|
||||
update_agent_file "$CURSOR_FILE" "Cursor IDE"
|
||||
;;
|
||||
qwen)
|
||||
@@ -581,12 +615,27 @@ update_specific_agent() {
|
||||
roo)
|
||||
update_agent_file "$ROO_FILE" "Roo Code"
|
||||
;;
|
||||
codebuddy)
|
||||
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
||||
;;
|
||||
qoder)
|
||||
update_agent_file "$QODER_FILE" "Qoder CLI"
|
||||
;;
|
||||
amp)
|
||||
update_agent_file "$AMP_FILE" "Amp"
|
||||
;;
|
||||
shai)
|
||||
update_agent_file "$SHAI_FILE" "SHAI"
|
||||
;;
|
||||
q)
|
||||
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 "Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo|q"
|
||||
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|shai|q|bob|qoder"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -646,11 +695,31 @@ update_all_existing_agents() {
|
||||
found_agent=true
|
||||
fi
|
||||
|
||||
if [[ -f "$CODEBUDDY_FILE" ]]; then
|
||||
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
||||
found_agent=true
|
||||
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
|
||||
update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
|
||||
found_agent=true
|
||||
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 [[ "$found_agent" == false ]]; then
|
||||
log_info "No existing agent files found, creating default Claude file..."
|
||||
@@ -674,7 +743,8 @@ print_summary() {
|
||||
fi
|
||||
|
||||
echo
|
||||
log_info "Usage: $0 [claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|q]"
|
||||
|
||||
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|shai|q|bob|qoder]"
|
||||
}
|
||||
|
||||
#==============================================================================
|
||||
@@ -726,3 +796,4 @@ main() {
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
|
||||
@@ -145,4 +145,4 @@ if ($Json) {
|
||||
if ($IncludeTasks) {
|
||||
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,3 +134,4 @@ function Test-DirHasFiles {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,20 +3,41 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$Json,
|
||||
[string]$ShortName,
|
||||
[int]$Number = 0,
|
||||
[switch]$Help,
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[string[]]$FeatureDescription
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Show help if requested
|
||||
if ($Help) {
|
||||
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] [-Number N] <feature description>"
|
||||
Write-Host ""
|
||||
Write-Host "Options:"
|
||||
Write-Host " -Json Output in JSON format"
|
||||
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 ""
|
||||
Write-Host "Examples:"
|
||||
Write-Host " ./create-new-feature.ps1 'Add user authentication system' -ShortName 'user-auth'"
|
||||
Write-Host " ./create-new-feature.ps1 'Implement OAuth2 integration for API'"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Check if feature description provided
|
||||
if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
|
||||
Write-Error "Usage: ./create-new-feature.ps1 [-Json] <feature description>"
|
||||
Write-Error "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$featureDesc = ($FeatureDescription -join ' ').Trim()
|
||||
|
||||
# Resolve repository root. Prefer git information when available, but fall back
|
||||
# to searching for repository markers so the workflow still functions in repositories that
|
||||
# were initialised with --no-git.
|
||||
# were initialized with --no-git.
|
||||
function Find-RepositoryRoot {
|
||||
param(
|
||||
[string]$StartDir,
|
||||
@@ -37,6 +58,77 @@ function Find-RepositoryRoot {
|
||||
$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]$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
|
||||
}
|
||||
|
||||
# Get highest number from ALL branches (not just matching short name)
|
||||
$highestBranch = Get-HighestNumberFromBranches
|
||||
|
||||
# Get highest number from ALL specs (not just matching short name)
|
||||
$highestSpec = Get-HighestNumberFromSpecs -SpecsDir $SpecsDir
|
||||
|
||||
# Take the maximum of both
|
||||
$maxNum = [Math]::Max($highestBranch, $highestSpec)
|
||||
|
||||
# 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)
|
||||
if (-not $fallbackRoot) {
|
||||
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
|
||||
@@ -60,21 +152,94 @@ Set-Location $repoRoot
|
||||
$specsDir = Join-Path $repoRoot 'specs'
|
||||
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
|
||||
|
||||
$highest = 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 }
|
||||
# Function to generate branch name with stop word filtering and length filtering
|
||||
function Get-BranchName {
|
||||
param([string]$Description)
|
||||
|
||||
# Common stop words to filter out
|
||||
$stopWords = @(
|
||||
'i', 'a', 'an', 'the', 'to', 'for', 'of', 'in', 'on', 'at', 'by', 'with', 'from',
|
||||
'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
|
||||
'do', 'does', 'did', 'will', 'would', 'should', 'could', 'can', 'may', 'might', 'must', 'shall',
|
||||
'this', 'that', 'these', 'those', 'my', 'your', 'our', 'their',
|
||||
'want', 'need', 'add', 'get', 'set'
|
||||
)
|
||||
|
||||
# Convert to lowercase and extract words (alphanumeric only)
|
||||
$cleanName = $Description.ToLower() -replace '[^a-z0-9\s]', ' '
|
||||
$words = $cleanName -split '\s+' | Where-Object { $_ }
|
||||
|
||||
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
|
||||
$meaningfulWords = @()
|
||||
foreach ($word in $words) {
|
||||
# Skip stop words
|
||||
if ($stopWords -contains $word) { continue }
|
||||
|
||||
# Keep words that are length >= 3 OR appear as uppercase in original (likely acronyms)
|
||||
if ($word.Length -ge 3) {
|
||||
$meaningfulWords += $word
|
||||
} elseif ($Description -match "\b$($word.ToUpper())\b") {
|
||||
# Keep short words if they appear as uppercase in original (likely acronyms)
|
||||
$meaningfulWords += $word
|
||||
}
|
||||
}
|
||||
|
||||
# If we have meaningful words, use first 3-4 of them
|
||||
if ($meaningfulWords.Count -gt 0) {
|
||||
$maxWords = if ($meaningfulWords.Count -eq 4) { 4 } else { 3 }
|
||||
$result = ($meaningfulWords | Select-Object -First $maxWords) -join '-'
|
||||
return $result
|
||||
} else {
|
||||
# Fallback to original logic if no meaningful words found
|
||||
$result = ConvertTo-CleanBranchName -Name $Description
|
||||
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
|
||||
return [string]::Join('-', $fallbackWords)
|
||||
}
|
||||
}
|
||||
$next = $highest + 1
|
||||
$featureNum = ('{0:000}' -f $next)
|
||||
|
||||
$branchName = $featureDesc.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
||||
$words = ($branchName -split '-') | Where-Object { $_ } | Select-Object -First 3
|
||||
$branchName = "$featureNum-$([string]::Join('-', $words))"
|
||||
# Generate branch name
|
||||
if ($ShortName) {
|
||||
# Use provided short name, just clean it up
|
||||
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName
|
||||
} else {
|
||||
# Generate from description with smart filtering
|
||||
$branchSuffix = Get-BranchName -Description $featureDesc
|
||||
}
|
||||
|
||||
# Determine branch number
|
||||
if ($Number -eq 0) {
|
||||
if ($hasGit) {
|
||||
# Check existing branches on remotes
|
||||
$Number = Get-NextBranchNumber -SpecsDir $specsDir
|
||||
} else {
|
||||
# Fall back to local directory check
|
||||
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
|
||||
}
|
||||
}
|
||||
|
||||
$featureNum = ('{0:000}' -f $Number)
|
||||
$branchName = "$featureNum-$branchSuffix"
|
||||
|
||||
# GitHub enforces a 244-byte limit on branch names
|
||||
# Validate and truncate if necessary
|
||||
$maxBranchLength = 244
|
||||
if ($branchName.Length -gt $maxBranchLength) {
|
||||
# Calculate how much we need to trim from suffix
|
||||
# Account for: feature number (3) + hyphen (1) = 4 chars
|
||||
$maxSuffixLength = $maxBranchLength - 4
|
||||
|
||||
# Truncate suffix
|
||||
$truncatedSuffix = $branchSuffix.Substring(0, [Math]::Min($branchSuffix.Length, $maxSuffixLength))
|
||||
# Remove trailing hyphen if truncation created one
|
||||
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
|
||||
|
||||
$originalBranchName = $branchName
|
||||
$branchName = "$featureNum-$truncatedSuffix"
|
||||
|
||||
Write-Warning "[specify] Branch name exceeded GitHub's 244-byte limit"
|
||||
Write-Warning "[specify] Original: $originalBranchName ($($originalBranchName.Length) bytes)"
|
||||
Write-Warning "[specify] Truncated to: $branchName ($($branchName.Length) bytes)"
|
||||
}
|
||||
|
||||
if ($hasGit) {
|
||||
try {
|
||||
@@ -115,3 +280,4 @@ if ($Json) {
|
||||
Write-Output "HAS_GIT: $hasGit"
|
||||
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
|
||||
}
|
||||
|
||||
|
||||
@@ -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, qwen, opencode, codex, windsurf, kilocode, auggie, roo, q)
|
||||
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, q, bob, qoder)
|
||||
|
||||
.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','qwen','opencode','codex','windsurf','kilocode','auggie','roo','q')]
|
||||
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','q','bob','qoder')]
|
||||
[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/agents/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'
|
||||
@@ -54,7 +54,12 @@ $WINDSURF_FILE = Join-Path $REPO_ROOT '.windsurf/rules/specify-rules.md'
|
||||
$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'
|
||||
$QODER_FILE = Join-Path $REPO_ROOT 'QODER.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'
|
||||
$BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||
|
||||
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
|
||||
|
||||
@@ -369,7 +374,7 @@ function Update-SpecificAgent {
|
||||
'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' }
|
||||
'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' }
|
||||
'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' }
|
||||
'cursor' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' }
|
||||
'cursor-agent' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' }
|
||||
'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' }
|
||||
'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' }
|
||||
'codex' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex CLI' }
|
||||
@@ -377,8 +382,13 @@ function Update-SpecificAgent {
|
||||
'kilocode' { Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code' }
|
||||
'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' }
|
||||
'qoder' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' }
|
||||
'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' }
|
||||
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo|q'; return $false }
|
||||
'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|shai|q|bob|qoder'; return $false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +405,11 @@ function Update-AllExistingAgents {
|
||||
if (Test-Path $KILOCODE_FILE) { if (-not (Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code')) { $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 $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 $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true }
|
||||
if (-not $found) {
|
||||
Write-Info 'No existing agent files found, creating default Claude file...'
|
||||
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
|
||||
@@ -410,7 +424,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|qwen|opencode|codex|windsurf|kilocode|auggie|roo|q]'
|
||||
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]'
|
||||
}
|
||||
|
||||
function Main {
|
||||
@@ -431,3 +445,4 @@ function Main {
|
||||
}
|
||||
|
||||
Main
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ The templates include comprehensive checklists that act as "unit tests" for the
|
||||
|
||||
```markdown
|
||||
### Requirement Completeness
|
||||
|
||||
- [ ] No [NEEDS CLARIFICATION] markers remain
|
||||
- [ ] Requirements are testable and unambiguous
|
||||
- [ ] Success criteria are measurable
|
||||
@@ -208,10 +209,14 @@ The implementation plan template enforces architectural principles through phase
|
||||
|
||||
```markdown
|
||||
### Phase -1: Pre-Implementation Gates
|
||||
|
||||
#### Simplicity Gate (Article VII)
|
||||
|
||||
- [ ] Using ≤3 projects?
|
||||
- [ ] No future-proofing?
|
||||
|
||||
#### Anti-Abstraction Gate (Article VIII)
|
||||
|
||||
- [ ] Using framework directly?
|
||||
- [ ] Single model representation?
|
||||
```
|
||||
@@ -347,15 +352,19 @@ The implementation plan template operationalizes these articles through concrete
|
||||
|
||||
```markdown
|
||||
### Phase -1: Pre-Implementation Gates
|
||||
|
||||
#### Simplicity Gate (Article VII)
|
||||
|
||||
- [ ] Using ≤3 projects?
|
||||
- [ ] No future-proofing?
|
||||
|
||||
#### Anti-Abstraction Gate (Article VIII)
|
||||
|
||||
- [ ] Using framework directly?
|
||||
- [ ] Single model representation?
|
||||
|
||||
#### Integration-First Gate (Article IX)
|
||||
|
||||
- [ ] Contracts defined?
|
||||
- [ ] Contract tests written?
|
||||
```
|
||||
|
||||
@@ -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,19 +65,167 @@ def _github_auth_headers(cli_token: str | None = None) -> dict:
|
||||
token = _github_token(cli_token)
|
||||
return {"Authorization": f"Bearer {token}"} if token else {}
|
||||
|
||||
AI_CHOICES = {
|
||||
"copilot": "GitHub Copilot",
|
||||
"claude": "Claude Code",
|
||||
"gemini": "Gemini CLI",
|
||||
"cursor": "Cursor",
|
||||
"qwen": "Qwen Code",
|
||||
"opencode": "opencode",
|
||||
"codex": "Codex CLI",
|
||||
"windsurf": "Windsurf",
|
||||
"kilocode": "Kilo Code",
|
||||
"auggie": "Auggie CLI",
|
||||
"roo": "Roo Code",
|
||||
"q": "Amazon Q Developer CLI",
|
||||
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": {
|
||||
"name": "GitHub Copilot",
|
||||
"folder": ".github/",
|
||||
"install_url": None, # IDE-based, no CLI check needed
|
||||
"requires_cli": False,
|
||||
},
|
||||
"claude": {
|
||||
"name": "Claude Code",
|
||||
"folder": ".claude/",
|
||||
"install_url": "https://docs.anthropic.com/en/docs/claude-code/setup",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"gemini": {
|
||||
"name": "Gemini CLI",
|
||||
"folder": ".gemini/",
|
||||
"install_url": "https://github.com/google-gemini/gemini-cli",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"cursor-agent": {
|
||||
"name": "Cursor",
|
||||
"folder": ".cursor/",
|
||||
"install_url": None, # IDE-based
|
||||
"requires_cli": False,
|
||||
},
|
||||
"qwen": {
|
||||
"name": "Qwen Code",
|
||||
"folder": ".qwen/",
|
||||
"install_url": "https://github.com/QwenLM/qwen-code",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"opencode": {
|
||||
"name": "opencode",
|
||||
"folder": ".opencode/",
|
||||
"install_url": "https://opencode.ai",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"codex": {
|
||||
"name": "Codex CLI",
|
||||
"folder": ".codex/",
|
||||
"install_url": "https://github.com/openai/codex",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"windsurf": {
|
||||
"name": "Windsurf",
|
||||
"folder": ".windsurf/",
|
||||
"install_url": None, # IDE-based
|
||||
"requires_cli": False,
|
||||
},
|
||||
"kilocode": {
|
||||
"name": "Kilo Code",
|
||||
"folder": ".kilocode/",
|
||||
"install_url": None, # IDE-based
|
||||
"requires_cli": False,
|
||||
},
|
||||
"auggie": {
|
||||
"name": "Auggie CLI",
|
||||
"folder": ".augment/",
|
||||
"install_url": "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"codebuddy": {
|
||||
"name": "CodeBuddy",
|
||||
"folder": ".codebuddy/",
|
||||
"install_url": "https://www.codebuddy.ai/cli",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"qoder": {
|
||||
"name": "Qoder CLI",
|
||||
"folder": ".qoder/",
|
||||
"install_url": "https://qoder.com/cli",
|
||||
"requires_cli": True,
|
||||
},
|
||||
"roo": {
|
||||
"name": "Roo Code",
|
||||
"folder": ".roo/",
|
||||
"install_url": None, # IDE-based
|
||||
"requires_cli": False,
|
||||
},
|
||||
"q": {
|
||||
"name": "Amazon Q Developer CLI",
|
||||
"folder": ".amazonq/",
|
||||
"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,
|
||||
},
|
||||
"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"}
|
||||
@@ -131,7 +280,7 @@ class StepTracker:
|
||||
s["detail"] = detail
|
||||
self._maybe_refresh()
|
||||
return
|
||||
# If not present, add it
|
||||
|
||||
self.steps.append({"key": key, "label": key, "status": status, "detail": detail})
|
||||
self._maybe_refresh()
|
||||
|
||||
@@ -148,7 +297,6 @@ class StepTracker:
|
||||
label = step["label"]
|
||||
detail_text = step["detail"].strip() if step["detail"] else ""
|
||||
|
||||
# Circles (unchanged styling)
|
||||
status = step["status"]
|
||||
if status == "done":
|
||||
symbol = "[green]●[/green]"
|
||||
@@ -272,7 +420,6 @@ def select_with_arrows(options: dict, prompt_text: str = "Select an option", def
|
||||
console.print("\n[red]Selection failed.[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Suppress explicit selection print; tracker / later logic will report consolidated status
|
||||
return selected_key
|
||||
|
||||
console = Console()
|
||||
@@ -296,7 +443,6 @@ app = typer.Typer(
|
||||
|
||||
def show_banner():
|
||||
"""Display the ASCII art banner."""
|
||||
# Create gradient effect with different colors
|
||||
banner_lines = BANNER.strip().split('\n')
|
||||
colors = ["bright_blue", "blue", "cyan", "bright_cyan", "white", "bright_white"]
|
||||
|
||||
@@ -312,8 +458,6 @@ def show_banner():
|
||||
@app.callback()
|
||||
def callback(ctx: typer.Context):
|
||||
"""Show banner when no subcommand is provided."""
|
||||
# Show banner only when no subcommand and no help flag
|
||||
# (help is handled by BannerGroup)
|
||||
if ctx.invoked_subcommand is None and "--help" not in sys.argv and "-h" not in sys.argv:
|
||||
show_banner()
|
||||
console.print(Align.center("[dim]Run 'specify --help' for usage information[/dim]"))
|
||||
@@ -337,18 +481,16 @@ def run_command(cmd: list[str], check_return: bool = True, capture: bool = False
|
||||
raise
|
||||
return None
|
||||
|
||||
def check_tool_for_tracker(tool: str, tracker: StepTracker) -> bool:
|
||||
"""Check if a tool is installed and update tracker."""
|
||||
if shutil.which(tool):
|
||||
tracker.complete(tool, "available")
|
||||
return True
|
||||
else:
|
||||
tracker.error(tool, "not found")
|
||||
return False
|
||||
|
||||
def check_tool(tool: str, install_hint: str) -> bool:
|
||||
"""Check if a tool is installed."""
|
||||
def check_tool(tool: str, tracker: StepTracker = None) -> bool:
|
||||
"""Check if a tool is installed. Optionally update tracker.
|
||||
|
||||
Args:
|
||||
tool: Name of the tool to check
|
||||
tracker: Optional StepTracker to update with results
|
||||
|
||||
Returns:
|
||||
True if tool is found, False otherwise
|
||||
"""
|
||||
# Special handling for Claude CLI after `claude migrate-installer`
|
||||
# See: https://github.com/github/spec-kit/issues/123
|
||||
# The migrate-installer command REMOVES the original executable from PATH
|
||||
@@ -356,12 +498,19 @@ def check_tool(tool: str, install_hint: str) -> bool:
|
||||
# This path should be prioritized over other claude executables in PATH
|
||||
if tool == "claude":
|
||||
if CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file():
|
||||
if tracker:
|
||||
tracker.complete(tool, "available")
|
||||
return True
|
||||
|
||||
if shutil.which(tool):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
found = shutil.which(tool) is not None
|
||||
|
||||
if tracker:
|
||||
if found:
|
||||
tracker.complete(tool, "available")
|
||||
else:
|
||||
tracker.error(tool, "not found")
|
||||
|
||||
return found
|
||||
|
||||
def is_git_repo(path: Path = None) -> bool:
|
||||
"""Check if the specified path is inside a git repository."""
|
||||
@@ -383,29 +532,108 @@ def is_git_repo(path: Path = None) -> bool:
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return False
|
||||
|
||||
def init_git_repo(project_path: Path, quiet: bool = False) -> bool:
|
||||
def init_git_repo(project_path: Path, quiet: bool = False) -> Tuple[bool, Optional[str]]:
|
||||
"""Initialize a git repository in the specified path.
|
||||
quiet: if True suppress console output (tracker handles status)
|
||||
|
||||
Args:
|
||||
project_path: Path to initialize git repository in
|
||||
quiet: if True suppress console output (tracker handles status)
|
||||
|
||||
Returns:
|
||||
Tuple of (success: bool, error_message: Optional[str])
|
||||
"""
|
||||
try:
|
||||
original_cwd = Path.cwd()
|
||||
os.chdir(project_path)
|
||||
if not quiet:
|
||||
console.print("[cyan]Initializing git repository...[/cyan]")
|
||||
subprocess.run(["git", "init"], check=True, capture_output=True)
|
||||
subprocess.run(["git", "add", "."], check=True, capture_output=True)
|
||||
subprocess.run(["git", "commit", "-m", "Initial commit from Specify template"], check=True, capture_output=True)
|
||||
subprocess.run(["git", "init"], check=True, capture_output=True, text=True)
|
||||
subprocess.run(["git", "add", "."], check=True, capture_output=True, text=True)
|
||||
subprocess.run(["git", "commit", "-m", "Initial commit from Specify template"], check=True, capture_output=True, text=True)
|
||||
if not quiet:
|
||||
console.print("[green]✓[/green] Git repository initialized")
|
||||
return True
|
||||
return True, None
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = f"Command: {' '.join(e.cmd)}\nExit code: {e.returncode}"
|
||||
if e.stderr:
|
||||
error_msg += f"\nError: {e.stderr.strip()}"
|
||||
elif e.stdout:
|
||||
error_msg += f"\nOutput: {e.stdout.strip()}"
|
||||
|
||||
if not quiet:
|
||||
console.print(f"[red]Error initializing git repository:[/red] {e}")
|
||||
return False
|
||||
return False, error_msg
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
def handle_vscode_settings(sub_item, dest_file, rel_path, verbose=False, tracker=None) -> None:
|
||||
"""Handle merging or copying of .vscode/settings.json files."""
|
||||
def log(message, color="green"):
|
||||
if verbose and not tracker:
|
||||
console.print(f"[{color}]{message}[/] {rel_path}")
|
||||
|
||||
try:
|
||||
with open(sub_item, 'r', encoding='utf-8') as f:
|
||||
new_settings = json.load(f)
|
||||
|
||||
if dest_file.exists():
|
||||
merged = merge_json_files(dest_file, new_settings, verbose=verbose and not tracker)
|
||||
with open(dest_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(merged, f, indent=4)
|
||||
f.write('\n')
|
||||
log("Merged:", "green")
|
||||
else:
|
||||
shutil.copy2(sub_item, dest_file)
|
||||
log("Copied (no existing settings.json):", "blue")
|
||||
|
||||
except Exception as e:
|
||||
log(f"Warning: Could not merge, copying instead: {e}", "yellow")
|
||||
shutil.copy2(sub_item, dest_file)
|
||||
|
||||
def merge_json_files(existing_path: Path, new_content: dict, verbose: bool = False) -> dict:
|
||||
"""Merge new JSON content into existing JSON file.
|
||||
|
||||
Performs a deep merge where:
|
||||
- New keys are added
|
||||
- Existing keys are preserved unless overwritten by new content
|
||||
- Nested dictionaries are merged recursively
|
||||
- Lists and other values are replaced (not merged)
|
||||
|
||||
Args:
|
||||
existing_path: Path to existing JSON file
|
||||
new_content: New JSON content to merge in
|
||||
verbose: Whether to print merge details
|
||||
|
||||
Returns:
|
||||
Merged JSON content as dict
|
||||
"""
|
||||
try:
|
||||
with open(existing_path, 'r', encoding='utf-8') as f:
|
||||
existing_content = json.load(f)
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
# If file doesn't exist or is invalid, just use new content
|
||||
return new_content
|
||||
|
||||
def deep_merge(base: dict, update: dict) -> dict:
|
||||
"""Recursively merge update dict into base dict."""
|
||||
result = base.copy()
|
||||
for key, value in update.items():
|
||||
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
||||
# Recursively merge nested dictionaries
|
||||
result[key] = deep_merge(result[key], value)
|
||||
else:
|
||||
# Add new key or replace existing value
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
merged = deep_merge(existing_content, new_content)
|
||||
|
||||
if verbose:
|
||||
console.print(f"[cyan]Merged JSON file:[/cyan] {existing_path.name}")
|
||||
|
||||
return merged
|
||||
|
||||
def download_template_from_github(ai_assistant: str, download_dir: Path, *, script_type: str = "sh", verbose: bool = True, show_progress: bool = True, client: httpx.Client = None, debug: bool = False, github_token: str = None) -> Tuple[Path, dict]:
|
||||
repo_owner = "github"
|
||||
repo_name = "spec-kit"
|
||||
@@ -425,10 +653,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:
|
||||
@@ -438,7 +667,6 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, scri
|
||||
console.print(Panel(str(e), title="Fetch Error", border_style="red"))
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Find the template asset for the specified AI assistant
|
||||
assets = release_data.get("assets", [])
|
||||
pattern = f"spec-kit-template-{ai_assistant}-{script_type}"
|
||||
matching_assets = [
|
||||
@@ -476,8 +704,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:
|
||||
@@ -523,7 +754,6 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
"""
|
||||
current_dir = Path.cwd()
|
||||
|
||||
# Step: fetch + download combined
|
||||
if tracker:
|
||||
tracker.start("fetch", "contacting GitHub API")
|
||||
try:
|
||||
@@ -556,12 +786,10 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
console.print("Extracting template...")
|
||||
|
||||
try:
|
||||
# Create project directory only if not using current directory
|
||||
if not is_current_dir:
|
||||
project_path.mkdir(parents=True)
|
||||
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
# List all files in the ZIP for debugging
|
||||
zip_contents = zip_ref.namelist()
|
||||
if tracker:
|
||||
tracker.start("zip-list")
|
||||
@@ -569,13 +797,11 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
elif verbose:
|
||||
console.print(f"[cyan]ZIP contains {len(zip_contents)} items[/cyan]")
|
||||
|
||||
# For current directory, extract to a temp location first
|
||||
if is_current_dir:
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
zip_ref.extractall(temp_path)
|
||||
|
||||
# Check what was extracted
|
||||
extracted_items = list(temp_path.iterdir())
|
||||
if tracker:
|
||||
tracker.start("extracted-summary")
|
||||
@@ -583,7 +809,6 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
elif verbose:
|
||||
console.print(f"[cyan]Extracted {len(extracted_items)} items to temp location[/cyan]")
|
||||
|
||||
# Handle GitHub-style ZIP with a single root directory
|
||||
source_dir = temp_path
|
||||
if len(extracted_items) == 1 and extracted_items[0].is_dir():
|
||||
source_dir = extracted_items[0]
|
||||
@@ -593,20 +818,22 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
elif verbose:
|
||||
console.print(f"[cyan]Found nested directory structure[/cyan]")
|
||||
|
||||
# Copy contents to current directory
|
||||
for item in source_dir.iterdir():
|
||||
dest_path = project_path / item.name
|
||||
if item.is_dir():
|
||||
if dest_path.exists():
|
||||
if verbose and not tracker:
|
||||
console.print(f"[yellow]Merging directory:[/yellow] {item.name}")
|
||||
# Recursively copy directory contents
|
||||
for sub_item in item.rglob('*'):
|
||||
if sub_item.is_file():
|
||||
rel_path = sub_item.relative_to(item)
|
||||
dest_file = dest_path / rel_path
|
||||
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(sub_item, dest_file)
|
||||
# Special handling for .vscode/settings.json - merge instead of overwrite
|
||||
if dest_file.name == "settings.json" and dest_file.parent.name == ".vscode":
|
||||
handle_vscode_settings(sub_item, dest_file, rel_path, verbose, tracker)
|
||||
else:
|
||||
shutil.copy2(sub_item, dest_file)
|
||||
else:
|
||||
shutil.copytree(item, dest_path)
|
||||
else:
|
||||
@@ -616,10 +843,8 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
if verbose and not tracker:
|
||||
console.print(f"[cyan]Template files merged into current directory[/cyan]")
|
||||
else:
|
||||
# Extract directly to project directory (original behavior)
|
||||
zip_ref.extractall(project_path)
|
||||
|
||||
# Check what was extracted
|
||||
extracted_items = list(project_path.iterdir())
|
||||
if tracker:
|
||||
tracker.start("extracted-summary")
|
||||
@@ -629,16 +854,14 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
for item in extracted_items:
|
||||
console.print(f" - {item.name} ({'dir' if item.is_dir() else 'file'})")
|
||||
|
||||
# Handle GitHub-style ZIP with a single root directory
|
||||
if len(extracted_items) == 1 and extracted_items[0].is_dir():
|
||||
# Move contents up one level
|
||||
nested_dir = extracted_items[0]
|
||||
temp_move_dir = project_path.parent / f"{project_path.name}_temp"
|
||||
# Move the nested directory contents to temp location
|
||||
|
||||
shutil.move(str(nested_dir), str(temp_move_dir))
|
||||
# Remove the now-empty project directory
|
||||
|
||||
project_path.rmdir()
|
||||
# Rename temp directory to project directory
|
||||
|
||||
shutil.move(str(temp_move_dir), str(project_path))
|
||||
if tracker:
|
||||
tracker.add("flatten", "Flatten nested directory")
|
||||
@@ -654,7 +877,7 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
console.print(f"[red]Error extracting template:[/red] {e}")
|
||||
if debug:
|
||||
console.print(Panel(str(e), title="Extraction Error", border_style="red"))
|
||||
# Clean up project directory if created and not current directory
|
||||
|
||||
if not is_current_dir and project_path.exists():
|
||||
shutil.rmtree(project_path)
|
||||
raise typer.Exit(1)
|
||||
@@ -664,7 +887,7 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
||||
finally:
|
||||
if tracker:
|
||||
tracker.add("cleanup", "Remove temporary archive")
|
||||
# Clean up downloaded ZIP file
|
||||
|
||||
if zip_path.exists():
|
||||
zip_path.unlink()
|
||||
if tracker:
|
||||
@@ -722,7 +945,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, qwen, opencode, codex, windsurf, kilocode, auggie 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, shai, q, bob, or qoder "),
|
||||
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"),
|
||||
@@ -737,7 +960,7 @@ def init(
|
||||
|
||||
This command will:
|
||||
1. Check that required tools are installed (git is optional)
|
||||
2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf, Kilo Code, Auggie CLI, or Amazon Q Developer CLI)
|
||||
2. Let you choose your AI assistant
|
||||
3. Download the appropriate template from GitHub
|
||||
4. Extract the template to a new project directory or current directory
|
||||
5. Initialize a fresh git repository (if not --no-git and no existing repo)
|
||||
@@ -752,13 +975,13 @@ def init(
|
||||
specify init . # Initialize in current directory (interactive AI selection)
|
||||
specify init --here --ai claude # Alternative syntax for current directory
|
||||
specify init --here --ai codex
|
||||
specify init --here --ai codebuddy
|
||||
specify init --here
|
||||
specify init --here --force # Skip confirmation when current directory not empty
|
||||
"""
|
||||
|
||||
show_banner()
|
||||
|
||||
# Handle '.' as shorthand for current directory (equivalent to --here)
|
||||
if project_name == ".":
|
||||
here = True
|
||||
project_name = None # Clear project_name to use existing validation logic
|
||||
@@ -809,91 +1032,57 @@ def init(
|
||||
f"{'Working Path':<15} [dim]{current_dir}[/dim]",
|
||||
]
|
||||
|
||||
# Add target path only if different from working dir
|
||||
if not here:
|
||||
setup_lines.append(f"{'Target Path':<15} [dim]{project_path}[/dim]")
|
||||
|
||||
console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))
|
||||
|
||||
# Check git only if we might need it (not --no-git)
|
||||
# Only set to True if the user wants it and the tool is available
|
||||
should_init_git = False
|
||||
if not no_git:
|
||||
should_init_git = check_tool("git", "https://git-scm.com/downloads")
|
||||
should_init_git = check_tool("git")
|
||||
if not should_init_git:
|
||||
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
|
||||
|
||||
if ai_assistant:
|
||||
if ai_assistant not in AI_CHOICES:
|
||||
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AI_CHOICES.keys())}")
|
||||
if ai_assistant not in AGENT_CONFIG:
|
||||
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AGENT_CONFIG.keys())}")
|
||||
raise typer.Exit(1)
|
||||
selected_ai = ai_assistant
|
||||
else:
|
||||
# Use arrow-key selection interface
|
||||
# Create options dict for selection (agent_key: display_name)
|
||||
ai_choices = {key: config["name"] for key, config in AGENT_CONFIG.items()}
|
||||
selected_ai = select_with_arrows(
|
||||
AI_CHOICES,
|
||||
ai_choices,
|
||||
"Choose your AI assistant:",
|
||||
"copilot"
|
||||
)
|
||||
|
||||
# Check agent tools unless ignored
|
||||
if not ignore_agent_tools:
|
||||
agent_tool_missing = False
|
||||
install_url = ""
|
||||
if selected_ai == "claude":
|
||||
if not check_tool("claude", "https://docs.anthropic.com/en/docs/claude-code/setup"):
|
||||
install_url = "https://docs.anthropic.com/en/docs/claude-code/setup"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "gemini":
|
||||
if not check_tool("gemini", "https://github.com/google-gemini/gemini-cli"):
|
||||
install_url = "https://github.com/google-gemini/gemini-cli"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "qwen":
|
||||
if not check_tool("qwen", "https://github.com/QwenLM/qwen-code"):
|
||||
install_url = "https://github.com/QwenLM/qwen-code"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "opencode":
|
||||
if not check_tool("opencode", "https://opencode.ai"):
|
||||
install_url = "https://opencode.ai"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "codex":
|
||||
if not check_tool("codex", "https://github.com/openai/codex"):
|
||||
install_url = "https://github.com/openai/codex"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "auggie":
|
||||
if not check_tool("auggie", "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli"):
|
||||
install_url = "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli"
|
||||
agent_tool_missing = True
|
||||
elif selected_ai == "q":
|
||||
if not check_tool("q", "https://github.com/aws/amazon-q-developer-cli"):
|
||||
install_url = "https://aws.amazon.com/developer/learning/q-developer-cli/"
|
||||
agent_tool_missing = True
|
||||
# GitHub Copilot and Cursor checks are not needed as they're typically available in supported IDEs
|
||||
agent_config = AGENT_CONFIG.get(selected_ai)
|
||||
if agent_config and agent_config["requires_cli"]:
|
||||
install_url = agent_config["install_url"]
|
||||
if not check_tool(selected_ai):
|
||||
error_panel = Panel(
|
||||
f"[cyan]{selected_ai}[/cyan] not found\n"
|
||||
f"Install from: [cyan]{install_url}[/cyan]\n"
|
||||
f"{agent_config['name']} is required to continue with this project type.\n\n"
|
||||
"Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check",
|
||||
title="[red]Agent Detection Error[/red]",
|
||||
border_style="red",
|
||||
padding=(1, 2)
|
||||
)
|
||||
console.print()
|
||||
console.print(error_panel)
|
||||
raise typer.Exit(1)
|
||||
|
||||
if agent_tool_missing:
|
||||
error_panel = Panel(
|
||||
f"[cyan]{selected_ai}[/cyan] not found\n"
|
||||
f"Install with: [cyan]{install_url}[/cyan]\n"
|
||||
f"{AI_CHOICES[selected_ai]} is required to continue with this project type.\n\n"
|
||||
"Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check",
|
||||
title="[red]Agent Detection Error[/red]",
|
||||
border_style="red",
|
||||
padding=(1, 2)
|
||||
)
|
||||
console.print()
|
||||
console.print(error_panel)
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Determine script type (explicit, interactive, or OS default)
|
||||
if script_type:
|
||||
if script_type not in SCRIPT_TYPE_CHOICES:
|
||||
console.print(f"[red]Error:[/red] Invalid script type '{script_type}'. Choose from: {', '.join(SCRIPT_TYPE_CHOICES.keys())}")
|
||||
raise typer.Exit(1)
|
||||
selected_script = script_type
|
||||
else:
|
||||
# Auto-detect default
|
||||
default_script = "ps" if os.name == "nt" else "sh"
|
||||
# Provide interactive selection similar to AI if stdin is a TTY
|
||||
|
||||
if sys.stdin.isatty():
|
||||
selected_script = select_with_arrows(SCRIPT_TYPE_CHOICES, "Choose script type (or press Enter)", default_script)
|
||||
else:
|
||||
@@ -902,12 +1091,10 @@ def init(
|
||||
console.print(f"[cyan]Selected AI assistant:[/cyan] {selected_ai}")
|
||||
console.print(f"[cyan]Selected script type:[/cyan] {selected_script}")
|
||||
|
||||
# Download and set up project
|
||||
# New tree-based progress (no emojis); include earlier substeps
|
||||
tracker = StepTracker("Initialize Specify Project")
|
||||
# Flag to allow suppressing legacy headings
|
||||
|
||||
sys._specify_tracker_active = True
|
||||
# Pre steps recorded as completed before live rendering
|
||||
|
||||
tracker.add("precheck", "Check required tools")
|
||||
tracker.complete("precheck", "ok")
|
||||
tracker.add("ai-select", "Select AI assistant")
|
||||
@@ -927,30 +1114,31 @@ def init(
|
||||
]:
|
||||
tracker.add(key, label)
|
||||
|
||||
# Use transient so live tree is replaced by the final static render (avoids duplicate output)
|
||||
# Track git error message outside Live context so it persists
|
||||
git_error_message = None
|
||||
|
||||
with Live(tracker.render(), console=console, refresh_per_second=8, transient=True) as live:
|
||||
tracker.attach_refresh(lambda: live.update(tracker.render()))
|
||||
try:
|
||||
# Create a httpx client with verify based on skip_tls
|
||||
verify = not skip_tls
|
||||
local_ssl_context = ssl_context if verify else False
|
||||
local_client = httpx.Client(verify=local_ssl_context)
|
||||
|
||||
download_and_extract_template(project_path, selected_ai, selected_script, here, verbose=False, tracker=tracker, client=local_client, debug=debug, github_token=github_token)
|
||||
|
||||
# Ensure scripts are executable (POSIX)
|
||||
ensure_executable_scripts(project_path, tracker=tracker)
|
||||
|
||||
# Git step
|
||||
if not no_git:
|
||||
tracker.start("git")
|
||||
if is_git_repo(project_path):
|
||||
tracker.complete("git", "existing repo detected")
|
||||
elif should_init_git:
|
||||
if init_git_repo(project_path, quiet=True):
|
||||
success, error_msg = init_git_repo(project_path, quiet=True)
|
||||
if success:
|
||||
tracker.complete("git", "initialized")
|
||||
else:
|
||||
tracker.error("git", "init failed")
|
||||
git_error_message = error_msg
|
||||
else:
|
||||
tracker.skip("git", "git not available")
|
||||
else:
|
||||
@@ -973,31 +1161,32 @@ def init(
|
||||
shutil.rmtree(project_path)
|
||||
raise typer.Exit(1)
|
||||
finally:
|
||||
# Force final render
|
||||
pass
|
||||
|
||||
# Final static tree (ensures finished state visible after Live context ends)
|
||||
console.print(tracker.render())
|
||||
console.print("\n[bold green]Project ready.[/bold green]")
|
||||
|
||||
# Show git error details if initialization failed
|
||||
if git_error_message:
|
||||
console.print()
|
||||
git_error_panel = Panel(
|
||||
f"[yellow]Warning:[/yellow] Git repository initialization failed\n\n"
|
||||
f"{git_error_message}\n\n"
|
||||
f"[dim]You can initialize git manually later with:[/dim]\n"
|
||||
f"[cyan]cd {project_path if not here else '.'}[/cyan]\n"
|
||||
f"[cyan]git init[/cyan]\n"
|
||||
f"[cyan]git add .[/cyan]\n"
|
||||
f"[cyan]git commit -m \"Initial commit\"[/cyan]",
|
||||
title="[red]Git Initialization Failed[/red]",
|
||||
border_style="red",
|
||||
padding=(1, 2)
|
||||
)
|
||||
console.print(git_error_panel)
|
||||
|
||||
# Agent folder security notice
|
||||
agent_folder_map = {
|
||||
"claude": ".claude/",
|
||||
"gemini": ".gemini/",
|
||||
"cursor": ".cursor/",
|
||||
"qwen": ".qwen/",
|
||||
"opencode": ".opencode/",
|
||||
"codex": ".codex/",
|
||||
"windsurf": ".windsurf/",
|
||||
"kilocode": ".kilocode/",
|
||||
"auggie": ".augment/",
|
||||
"copilot": ".github/",
|
||||
"roo": ".roo/",
|
||||
"q": ".amazonq/"
|
||||
}
|
||||
|
||||
if selected_ai in agent_folder_map:
|
||||
agent_folder = agent_folder_map[selected_ai]
|
||||
agent_config = AGENT_CONFIG.get(selected_ai)
|
||||
if agent_config:
|
||||
agent_folder = agent_config["folder"]
|
||||
security_notice = Panel(
|
||||
f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n"
|
||||
f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
|
||||
@@ -1008,7 +1197,6 @@ def init(
|
||||
console.print()
|
||||
console.print(security_notice)
|
||||
|
||||
# Boxed "Next steps" section
|
||||
steps_lines = []
|
||||
if not here:
|
||||
steps_lines.append(f"1. Go to the project folder: [cyan]cd {project_name}[/cyan]")
|
||||
@@ -1061,32 +1249,28 @@ def check():
|
||||
tracker = StepTracker("Check Available Tools")
|
||||
|
||||
tracker.add("git", "Git version control")
|
||||
tracker.add("claude", "Claude Code CLI")
|
||||
tracker.add("gemini", "Gemini CLI")
|
||||
tracker.add("qwen", "Qwen Code CLI")
|
||||
tracker.add("code", "Visual Studio Code")
|
||||
tracker.add("code-insiders", "Visual Studio Code Insiders")
|
||||
tracker.add("cursor-agent", "Cursor IDE agent")
|
||||
tracker.add("windsurf", "Windsurf IDE")
|
||||
tracker.add("kilocode", "Kilo Code IDE")
|
||||
tracker.add("opencode", "opencode")
|
||||
tracker.add("codex", "Codex CLI")
|
||||
tracker.add("auggie", "Auggie CLI")
|
||||
tracker.add("q", "Amazon Q Developer CLI")
|
||||
git_ok = check_tool("git", tracker=tracker)
|
||||
|
||||
git_ok = check_tool_for_tracker("git", tracker)
|
||||
claude_ok = check_tool_for_tracker("claude", tracker)
|
||||
gemini_ok = check_tool_for_tracker("gemini", tracker)
|
||||
qwen_ok = check_tool_for_tracker("qwen", tracker)
|
||||
code_ok = check_tool_for_tracker("code", tracker)
|
||||
code_insiders_ok = check_tool_for_tracker("code-insiders", tracker)
|
||||
cursor_ok = check_tool_for_tracker("cursor-agent", tracker)
|
||||
windsurf_ok = check_tool_for_tracker("windsurf", tracker)
|
||||
kilocode_ok = check_tool_for_tracker("kilocode", tracker)
|
||||
opencode_ok = check_tool_for_tracker("opencode", tracker)
|
||||
codex_ok = check_tool_for_tracker("codex", tracker)
|
||||
auggie_ok = check_tool_for_tracker("auggie", tracker)
|
||||
q_ok = check_tool_for_tracker("q", tracker)
|
||||
agent_results = {}
|
||||
for agent_key, agent_config in AGENT_CONFIG.items():
|
||||
agent_name = agent_config["name"]
|
||||
requires_cli = agent_config["requires_cli"]
|
||||
|
||||
tracker.add(agent_key, agent_name)
|
||||
|
||||
if requires_cli:
|
||||
agent_results[agent_key] = check_tool(agent_key, tracker=tracker)
|
||||
else:
|
||||
# IDE-based agent - skip CLI check and mark as optional
|
||||
tracker.skip(agent_key, "IDE-based, no CLI check")
|
||||
agent_results[agent_key] = False # Don't count IDE agents as "found"
|
||||
|
||||
# Check VS Code variants (not in agent config)
|
||||
tracker.add("code", "Visual Studio Code")
|
||||
code_ok = check_tool("code", tracker=tracker)
|
||||
|
||||
tracker.add("code-insiders", "Visual Studio Code Insiders")
|
||||
code_insiders_ok = check_tool("code-insiders", tracker=tracker)
|
||||
|
||||
console.print(tracker.render())
|
||||
|
||||
@@ -1094,11 +1278,92 @@ def check():
|
||||
|
||||
if not git_ok:
|
||||
console.print("[dim]Tip: Install git for repository management[/dim]")
|
||||
if not (claude_ok or gemini_ok or cursor_ok or qwen_ok or windsurf_ok or kilocode_ok or opencode_ok or codex_ok or auggie_ok or q_ok):
|
||||
|
||||
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", platform.python_version())
|
||||
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()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
@@ -3,21 +3,26 @@
|
||||
Auto-generated from all feature plans. Last updated: [DATE]
|
||||
|
||||
## Active Technologies
|
||||
|
||||
[EXTRACTED FROM ALL PLAN.MD FILES]
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
|
||||
```text
|
||||
[ACTUAL STRUCTURE FROM PLANS]
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]
|
||||
|
||||
## Code Style
|
||||
|
||||
[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]
|
||||
|
||||
## Recent Changes
|
||||
|
||||
[LAST 3 FEATURES AND WHAT THEY ADDED]
|
||||
|
||||
<!-- MANUAL ADDITIONS START -->
|
||||
<!-- MANUAL ADDITIONS END -->
|
||||
<!-- MANUAL ADDITIONS END -->
|
||||
|
||||
@@ -15,13 +15,13 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
|
||||
## Goal
|
||||
|
||||
Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/tasks` has successfully produced a complete `tasks.md`.
|
||||
Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/speckit.tasks` has successfully produced a complete `tasks.md`.
|
||||
|
||||
## Operating Constraints
|
||||
|
||||
**STRICTLY READ-ONLY**: Do **not** modify any files. Output a structured analysis report. Offer an optional remediation plan (user must explicitly approve before any follow-up editing commands would be invoked manually).
|
||||
|
||||
**Constitution Authority**: The project constitution (`/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/analyze`.
|
||||
**Constitution Authority**: The project constitution (`/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/speckit.analyze`.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
@@ -157,9 +157,9 @@ Output a Markdown report (no file writes) with the following structure:
|
||||
|
||||
At end of report, output a concise Next Actions block:
|
||||
|
||||
- If CRITICAL issues exist: Recommend resolving before `/implement`
|
||||
- If CRITICAL issues exist: Recommend resolving before `/speckit.implement`
|
||||
- If only LOW/MEDIUM: User may proceed, but provide improvement suggestions
|
||||
- Provide explicit command suggestions: e.g., "Run /specify with refinement", "Run /plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'"
|
||||
- Provide explicit command suggestions: e.g., "Run /speckit.specify with refinement", "Run /speckit.plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'"
|
||||
|
||||
### 8. Offer Remediation
|
||||
|
||||
|
||||
@@ -10,12 +10,14 @@ scripts:
|
||||
**CRITICAL CONCEPT**: Checklists are **UNIT TESTS FOR REQUIREMENTS WRITING** - they validate the quality, clarity, and completeness of requirements in a given domain.
|
||||
|
||||
**NOT for verification/testing**:
|
||||
|
||||
- ❌ NOT "Verify the button clicks correctly"
|
||||
- ❌ NOT "Test error handling works"
|
||||
- ❌ NOT "Confirm the API returns 200"
|
||||
- ❌ NOT checking if code/implementation matches the spec
|
||||
|
||||
**FOR requirements quality validation**:
|
||||
|
||||
- ✅ "Are visual hierarchy requirements defined for all card types?" (completeness)
|
||||
- ✅ "Is 'prominent display' quantified with specific sizing/positioning?" (clarity)
|
||||
- ✅ "Are hover state requirements consistent across all interactive elements?" (consistency)
|
||||
@@ -80,7 +82,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- spec.md: Feature requirements and scope
|
||||
- plan.md (if exists): Technical details, dependencies
|
||||
- tasks.md (if exists): Implementation tasks
|
||||
|
||||
|
||||
**Context Loading Strategy**:
|
||||
- Load only necessary portions relevant to active focus areas (avoid full-file dumping)
|
||||
- Prefer summarizing long sections into concise scenario/requirement bullets
|
||||
@@ -91,7 +93,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- Create `FEATURE_DIR/checklists/` directory if it doesn't exist
|
||||
- Generate unique checklist filename:
|
||||
- Use short, descriptive name based on domain (e.g., `ux.md`, `api.md`, `security.md`)
|
||||
- Format: `[domain].md`
|
||||
- Format: `[domain].md`
|
||||
- If file exists, append to existing file
|
||||
- Number items sequentially starting from CHK001
|
||||
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists)
|
||||
@@ -103,7 +105,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- **Consistency**: Do requirements align with each other?
|
||||
- **Measurability**: Can requirements be objectively verified?
|
||||
- **Coverage**: Are all scenarios/edge cases addressed?
|
||||
|
||||
|
||||
**Category Structure** - Group items by requirement quality dimensions:
|
||||
- **Requirement Completeness** (Are all necessary requirements documented?)
|
||||
- **Requirement Clarity** (Are requirements specific and unambiguous?)
|
||||
@@ -114,14 +116,14 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- **Non-Functional Requirements** (Performance, Security, Accessibility, etc. - are they specified?)
|
||||
- **Dependencies & Assumptions** (Are they documented and validated?)
|
||||
- **Ambiguities & Conflicts** (What needs clarification?)
|
||||
|
||||
|
||||
**HOW TO WRITE CHECKLIST ITEMS - "Unit Tests for English"**:
|
||||
|
||||
|
||||
❌ **WRONG** (Testing implementation):
|
||||
- "Verify landing page displays 3 episode cards"
|
||||
- "Test hover states work on desktop"
|
||||
- "Confirm logo click navigates home"
|
||||
|
||||
|
||||
✅ **CORRECT** (Testing requirements quality):
|
||||
- "Are the exact number and layout of featured episodes specified?" [Completeness]
|
||||
- "Is 'prominent display' quantified with specific sizing/positioning?" [Clarity]
|
||||
@@ -130,7 +132,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- "Is the fallback behavior specified when logo image fails to load?" [Edge Cases]
|
||||
- "Are loading states defined for asynchronous episode data?" [Completeness]
|
||||
- "Does the spec define visual hierarchy for competing UI elements?" [Clarity]
|
||||
|
||||
|
||||
**ITEM STRUCTURE**:
|
||||
Each item should follow this pattern:
|
||||
- Question format asking about requirement quality
|
||||
@@ -138,28 +140,28 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- Include quality dimension in brackets [Completeness/Clarity/Consistency/etc.]
|
||||
- Reference spec section `[Spec §X.Y]` when checking existing requirements
|
||||
- Use `[Gap]` marker when checking for missing requirements
|
||||
|
||||
|
||||
**EXAMPLES BY QUALITY DIMENSION**:
|
||||
|
||||
|
||||
Completeness:
|
||||
- "Are error handling requirements defined for all API failure modes? [Gap]"
|
||||
- "Are accessibility requirements specified for all interactive elements? [Completeness]"
|
||||
- "Are mobile breakpoint requirements defined for responsive layouts? [Gap]"
|
||||
|
||||
|
||||
Clarity:
|
||||
- "Is 'fast loading' quantified with specific timing thresholds? [Clarity, Spec §NFR-2]"
|
||||
- "Are 'related episodes' selection criteria explicitly defined? [Clarity, Spec §FR-5]"
|
||||
- "Is 'prominent' defined with measurable visual properties? [Ambiguity, Spec §FR-4]"
|
||||
|
||||
|
||||
Consistency:
|
||||
- "Do navigation requirements align across all pages? [Consistency, Spec §FR-10]"
|
||||
- "Are card component requirements consistent between landing and detail pages? [Consistency]"
|
||||
|
||||
|
||||
Coverage:
|
||||
- "Are requirements defined for zero-state scenarios (no episodes)? [Coverage, Edge Case]"
|
||||
- "Are concurrent user interaction scenarios addressed? [Coverage, Gap]"
|
||||
- "Are requirements specified for partial data loading failures? [Coverage, Exception Flow]"
|
||||
|
||||
|
||||
Measurability:
|
||||
- "Are visual hierarchy requirements measurable/testable? [Acceptance Criteria, Spec §FR-1]"
|
||||
- "Can 'balanced visual weight' be objectively verified? [Measurability, Spec §FR-2]"
|
||||
@@ -195,7 +197,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- ❌ "Click", "navigate", "render", "load", "execute"
|
||||
- ❌ Test cases, test plans, QA procedures
|
||||
- ❌ Implementation details (frameworks, APIs, algorithms)
|
||||
|
||||
|
||||
**✅ REQUIRED PATTERNS** - These test requirements quality:
|
||||
- ✅ "Are [requirement type] defined/specified/documented for [scenario]?"
|
||||
- ✅ "Is [vague term] quantified/clarified with specific criteria?"
|
||||
@@ -225,6 +227,7 @@ To avoid clutter, use descriptive types and clean up obsolete checklists when do
|
||||
**UX Requirements Quality:** `ux.md`
|
||||
|
||||
Sample items (testing the requirements, NOT the implementation):
|
||||
|
||||
- "Are visual hierarchy requirements defined with measurable criteria? [Clarity, Spec §FR-1]"
|
||||
- "Is the number and positioning of UI elements explicitly specified? [Completeness, Spec §FR-1]"
|
||||
- "Are interaction state requirements (hover, focus, active) consistently defined? [Consistency]"
|
||||
@@ -235,6 +238,7 @@ Sample items (testing the requirements, NOT the implementation):
|
||||
**API Requirements Quality:** `api.md`
|
||||
|
||||
Sample items:
|
||||
|
||||
- "Are error response formats specified for all failure scenarios? [Completeness]"
|
||||
- "Are rate limiting requirements quantified with specific thresholds? [Clarity]"
|
||||
- "Are authentication requirements consistent across all endpoints? [Consistency]"
|
||||
@@ -244,6 +248,7 @@ Sample items:
|
||||
**Performance Requirements Quality:** `performance.md`
|
||||
|
||||
Sample items:
|
||||
|
||||
- "Are performance requirements quantified with specific metrics? [Clarity]"
|
||||
- "Are performance targets defined for all critical user journeys? [Coverage]"
|
||||
- "Are performance requirements under different load conditions specified? [Completeness]"
|
||||
@@ -253,6 +258,7 @@ Sample items:
|
||||
**Security Requirements Quality:** `security.md`
|
||||
|
||||
Sample items:
|
||||
|
||||
- "Are authentication requirements specified for all protected resources? [Coverage]"
|
||||
- "Are data protection requirements defined for sensitive information? [Completeness]"
|
||||
- "Is the threat model documented and requirements aligned to it? [Traceability]"
|
||||
@@ -282,9 +288,10 @@ Sample items:
|
||||
```
|
||||
|
||||
**Key Differences:**
|
||||
|
||||
- Wrong: Tests if the system works correctly
|
||||
- Correct: Tests if the requirements are written correctly
|
||||
- Wrong: Verification of behavior
|
||||
- Correct: Validation of requirement quality
|
||||
- Wrong: "Does it do X?"
|
||||
- Wrong: "Does it do X?"
|
||||
- Correct: "Is X clearly specified?"
|
||||
|
||||
@@ -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
|
||||
@@ -87,50 +91,63 @@ Execution steps:
|
||||
3. Generate (internally) a prioritized queue of candidate clarification questions (maximum 5). Do NOT output them all at once. Apply these constraints:
|
||||
- Maximum of 10 total questions across the whole session.
|
||||
- Each question must be answerable with EITHER:
|
||||
* A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
|
||||
* A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").
|
||||
- Only include questions whose answers materially impact architecture, data modeling, task decomposition, test design, UX behavior, operational readiness, or compliance validation.
|
||||
- Ensure category coverage balance: attempt to cover the highest impact unresolved categories first; avoid asking two low-impact questions when a single high-impact area (e.g., security posture) is unresolved.
|
||||
- Exclude questions already answered, trivial stylistic preferences, or plan-level execution details (unless blocking correctness).
|
||||
- Favor clarifications that reduce downstream rework risk or prevent misaligned acceptance tests.
|
||||
- If more than 5 categories remain unresolved, select the top 5 by (Impact * Uncertainty) heuristic.
|
||||
- A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
|
||||
- A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").
|
||||
- Only include questions whose answers materially impact architecture, data modeling, task decomposition, test design, UX behavior, operational readiness, or compliance validation.
|
||||
- Ensure category coverage balance: attempt to cover the highest impact unresolved categories first; avoid asking two low-impact questions when a single high-impact area (e.g., security posture) is unresolved.
|
||||
- Exclude questions already answered, trivial stylistic preferences, or plan-level execution details (unless blocking correctness).
|
||||
- Favor clarifications that reduce downstream rework risk or prevent misaligned acceptance tests.
|
||||
- If more than 5 categories remain unresolved, select the top 5 by (Impact * Uncertainty) heuristic.
|
||||
|
||||
4. Sequential questioning loop (interactive):
|
||||
- Present EXACTLY ONE question at a time.
|
||||
- For multiple‑choice questions render options as a Markdown table:
|
||||
- For multiple‑choice questions:
|
||||
- **Analyze all options** and determine the **most suitable option** based on:
|
||||
- Best practices for the project type
|
||||
- Common patterns in similar implementations
|
||||
- Risk reduction (security, performance, maintainability)
|
||||
- Alignment with any explicit project goals or constraints visible in the spec
|
||||
- Present your **recommended option prominently** at the top with clear reasoning (1-2 sentences explaining why this is the best choice).
|
||||
- Format as: `**Recommended:** Option [X] - <reasoning>`
|
||||
- Then render all options as a Markdown table:
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| A | <Option A description> |
|
||||
| B | <Option B description> |
|
||||
| C | <Option C description> | (add D/E as needed up to 5)
|
||||
| Short | Provide a different short answer (<=5 words) | (Include only if free-form alternative is appropriate)
|
||||
| C | <Option C description> (add D/E as needed up to 5) |
|
||||
| Short | Provide a different short answer (<=5 words) (Include only if free-form alternative is appropriate) |
|
||||
|
||||
- For short‑answer style (no meaningful discrete options), output a single line after the question: `Format: Short answer (<=5 words)`.
|
||||
- After the table, add: `You can reply with the option letter (e.g., "A"), accept the recommendation by saying "yes" or "recommended", or provide your own short answer.`
|
||||
- For short‑answer style (no meaningful discrete options):
|
||||
- Provide your **suggested answer** based on best practices and context.
|
||||
- Format as: `**Suggested:** <your proposed answer> - <brief reasoning>`
|
||||
- Then output: `Format: Short answer (<=5 words). You can accept the suggestion by saying "yes" or "suggested", or provide your own answer.`
|
||||
- After the user answers:
|
||||
* Validate the answer maps to one option or fits the <=5 word constraint.
|
||||
* If ambiguous, ask for a quick disambiguation (count still belongs to same question; do not advance).
|
||||
* Once satisfactory, record it in working memory (do not yet write to disk) and move to the next queued question.
|
||||
- If the user replies with "yes", "recommended", or "suggested", use your previously stated recommendation/suggestion as the answer.
|
||||
- Otherwise, validate the answer maps to one option or fits the <=5 word constraint.
|
||||
- If ambiguous, ask for a quick disambiguation (count still belongs to same question; do not advance).
|
||||
- Once satisfactory, record it in working memory (do not yet write to disk) and move to the next queued question.
|
||||
- Stop asking further questions when:
|
||||
* All critical ambiguities resolved early (remaining queued items become unnecessary), OR
|
||||
* User signals completion ("done", "good", "no more"), OR
|
||||
* You reach 5 asked questions.
|
||||
- All critical ambiguities resolved early (remaining queued items become unnecessary), OR
|
||||
- User signals completion ("done", "good", "no more"), OR
|
||||
- You reach 5 asked questions.
|
||||
- Never reveal future queued questions in advance.
|
||||
- If no valid questions exist at start, immediately report no critical ambiguities.
|
||||
|
||||
5. Integration after EACH accepted answer (incremental update approach):
|
||||
- Maintain in-memory representation of the spec (loaded once at start) plus the raw file contents.
|
||||
- For the first integrated answer in this session:
|
||||
* Ensure a `## Clarifications` section exists (create it just after the highest-level contextual/overview section per the spec template if missing).
|
||||
* Under it, create (if not present) a `### Session YYYY-MM-DD` subheading for today.
|
||||
- Ensure a `## Clarifications` section exists (create it just after the highest-level contextual/overview section per the spec template if missing).
|
||||
- Under it, create (if not present) a `### Session YYYY-MM-DD` subheading for today.
|
||||
- Append a bullet line immediately after acceptance: `- Q: <question> → A: <final answer>`.
|
||||
- Then immediately apply the clarification to the most appropriate section(s):
|
||||
* Functional ambiguity → Update or add a bullet in Functional Requirements.
|
||||
* User interaction / actor distinction → Update User Stories or Actors subsection (if present) with clarified role, constraint, or scenario.
|
||||
* Data shape / entities → Update Data Model (add fields, types, relationships) preserving ordering; note added constraints succinctly.
|
||||
* Non-functional constraint → Add/modify measurable criteria in Non-Functional / Quality Attributes section (convert vague adjective to metric or explicit target).
|
||||
* Edge case / negative flow → Add a new bullet under Edge Cases / Error Handling (or create such subsection if template provides placeholder for it).
|
||||
* Terminology conflict → Normalize term across spec; retain original only if necessary by adding `(formerly referred to as "X")` once.
|
||||
- Functional ambiguity → Update or add a bullet in Functional Requirements.
|
||||
- User interaction / actor distinction → Update User Stories or Actors subsection (if present) with clarified role, constraint, or scenario.
|
||||
- Data shape / entities → Update Data Model (add fields, types, relationships) preserving ordering; note added constraints succinctly.
|
||||
- Non-functional constraint → Add/modify measurable criteria in Non-Functional / Quality Attributes section (convert vague adjective to metric or explicit target).
|
||||
- Edge case / negative flow → Add a new bullet under Edge Cases / Error Handling (or create such subsection if template provides placeholder for it).
|
||||
- Terminology conflict → Normalize term across spec; retain original only if necessary by adding `(formerly referred to as "X")` once.
|
||||
- If the clarification invalidates an earlier ambiguous statement, replace that statement instead of duplicating; leave no obsolete contradictory text.
|
||||
- Save the spec file AFTER each integration to minimize risk of context loss (atomic overwrite).
|
||||
- Preserve formatting: do not reorder unrelated sections; keep heading hierarchy intact.
|
||||
@@ -155,12 +172,13 @@ Execution steps:
|
||||
- Suggested next command.
|
||||
|
||||
Behavior rules:
|
||||
|
||||
- If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding.
|
||||
- If spec file missing, instruct user to run `/speckit.specify` first (do not create a new spec here).
|
||||
- Never exceed 5 total asked questions (clarification retries for a single question do not count as new questions).
|
||||
- Avoid speculative tech stack questions unless the absence blocks functional clarity.
|
||||
- Respect user early termination signals ("stop", "done", "proceed").
|
||||
- If no questions asked due to full coverage, output a compact coverage summary (all categories Clear) then suggest advancing.
|
||||
- If quota reached with unresolved high-impact categories remaining, explicitly flag them under Deferred with rationale.
|
||||
- If no questions asked due to full coverage, output a compact coverage summary (all categories Clear) then suggest advancing.
|
||||
- If quota reached with unresolved high-impact categories remaining, explicitly flag them under Deferred with rationale.
|
||||
|
||||
Context for prioritization: {ARGS}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
---
|
||||
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
|
||||
@@ -25,9 +29,9 @@ Follow this execution flow:
|
||||
- Otherwise infer from existing repo context (README, docs, prior constitution versions if embedded).
|
||||
- For governance dates: `RATIFICATION_DATE` is the original adoption date (if unknown ask or mark TODO), `LAST_AMENDED_DATE` is today if changes are made, otherwise keep previous.
|
||||
- `CONSTITUTION_VERSION` must increment according to semantic versioning rules:
|
||||
* MAJOR: Backward incompatible governance/principle removals or redefinitions.
|
||||
* MINOR: New principle/section added or materially expanded guidance.
|
||||
* PATCH: Clarifications, wording, typo fixes, non-semantic refinements.
|
||||
- MAJOR: Backward incompatible governance/principle removals or redefinitions.
|
||||
- MINOR: New principle/section added or materially expanded guidance.
|
||||
- PATCH: Clarifications, wording, typo fixes, non-semantic refinements.
|
||||
- If version bump type ambiguous, propose reasoning before finalizing.
|
||||
|
||||
3. Draft the updated constitution content:
|
||||
@@ -65,6 +69,7 @@ Follow this execution flow:
|
||||
- Suggested commit message (e.g., `docs: amend constitution to vX.Y.Z (principle additions + governance update)`).
|
||||
|
||||
Formatting & Style Requirements:
|
||||
|
||||
- Use Markdown headings exactly as in the template (do not demote/promote levels).
|
||||
- Wrap long rationale lines to keep readability (<100 chars ideally) but do not hard enforce with awkward breaks.
|
||||
- Keep a single blank line between sections.
|
||||
|
||||
@@ -20,31 +20,33 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
2. **Check checklists status** (if FEATURE_DIR/checklists/ exists):
|
||||
- Scan all checklist files in the checklists/ directory
|
||||
- For each checklist, count:
|
||||
* Total items: All lines matching `- [ ]` or `- [X]` or `- [x]`
|
||||
* Completed items: Lines matching `- [X]` or `- [x]`
|
||||
* Incomplete items: Lines matching `- [ ]`
|
||||
- Total items: All lines matching `- [ ]` or `- [X]` or `- [x]`
|
||||
- Completed items: Lines matching `- [X]` or `- [x]`
|
||||
- Incomplete items: Lines matching `- [ ]`
|
||||
- Create a status table:
|
||||
```
|
||||
|
||||
```text
|
||||
| Checklist | Total | Completed | Incomplete | Status |
|
||||
|-----------|-------|-----------|------------|--------|
|
||||
| ux.md | 12 | 12 | 0 | ✓ PASS |
|
||||
| test.md | 8 | 5 | 3 | ✗ FAIL |
|
||||
| security.md | 6 | 6 | 0 | ✓ PASS |
|
||||
```
|
||||
|
||||
- Calculate overall status:
|
||||
* **PASS**: All checklists have 0 incomplete items
|
||||
* **FAIL**: One or more checklists have incomplete items
|
||||
|
||||
- **PASS**: All checklists have 0 incomplete items
|
||||
- **FAIL**: One or more checklists have incomplete items
|
||||
|
||||
- **If any checklist is incomplete**:
|
||||
* Display the table with incomplete item counts
|
||||
* **STOP** and ask: "Some checklists are incomplete. Do you want to proceed with implementation anyway? (yes/no)"
|
||||
* Wait for user response before continuing
|
||||
* If user says "no" or "wait" or "stop", halt execution
|
||||
* If user says "yes" or "proceed" or "continue", proceed to step 3
|
||||
|
||||
- Display the table with incomplete item counts
|
||||
- **STOP** and ask: "Some checklists are incomplete. Do you want to proceed with implementation anyway? (yes/no)"
|
||||
- Wait for user response before continuing
|
||||
- If user says "no" or "wait" or "stop", halt execution
|
||||
- If user says "yes" or "proceed" or "continue", proceed to step 3
|
||||
|
||||
- **If all checklists are complete**:
|
||||
* Display the table showing all checklists passed
|
||||
* Automatically proceed to step 3
|
||||
- Display the table showing all checklists passed
|
||||
- Automatically proceed to step 3
|
||||
|
||||
3. Load and analyze the implementation context:
|
||||
- **REQUIRED**: Read tasks.md for the complete task list and execution plan
|
||||
@@ -54,27 +56,71 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- **IF EXISTS**: Read research.md for technical decisions and constraints
|
||||
- **IF EXISTS**: Read quickstart.md for integration scenarios
|
||||
|
||||
4. Parse tasks.md structure and extract:
|
||||
4. **Project Setup Verification**:
|
||||
- **REQUIRED**: Create/verify ignore files based on actual project setup:
|
||||
|
||||
**Detection & Creation Logic**:
|
||||
- Check if the following command succeeds to determine if the repository is a git repo (create/verify .gitignore if so):
|
||||
|
||||
```sh
|
||||
git rev-parse --git-dir 2>/dev/null
|
||||
```
|
||||
|
||||
- Check if Dockerfile* exists or Docker in plan.md → create/verify .dockerignore
|
||||
- Check if .eslintrc* 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 .npmrc or package.json exists → create/verify .npmignore (if publishing)
|
||||
- Check if terraform files (*.tf) exist → create/verify .terraformignore
|
||||
- Check if .helmignore needed (helm charts present) → create/verify .helmignore
|
||||
|
||||
**If ignore file already exists**: Verify it contains essential patterns, append missing critical patterns only
|
||||
**If ignore file missing**: Create with full pattern set for detected technology
|
||||
|
||||
**Common Patterns by Technology** (from plan.md tech stack):
|
||||
- **Node.js/JavaScript/TypeScript**: `node_modules/`, `dist/`, `build/`, `*.log`, `.env*`
|
||||
- **Python**: `__pycache__/`, `*.pyc`, `.venv/`, `venv/`, `dist/`, `*.egg-info/`
|
||||
- **Java**: `target/`, `*.class`, `*.jar`, `.gradle/`, `build/`
|
||||
- **C#/.NET**: `bin/`, `obj/`, `*.user`, `*.suo`, `packages/`
|
||||
- **Go**: `*.exe`, `*.test`, `vendor/`, `*.out`
|
||||
- **Ruby**: `.bundle/`, `log/`, `tmp/`, `*.gem`, `vendor/bundle/`
|
||||
- **PHP**: `vendor/`, `*.log`, `*.cache`, `*.env`
|
||||
- **Rust**: `target/`, `debug/`, `release/`, `*.rs.bk`, `*.rlib`, `*.prof*`, `.idea/`, `*.log`, `.env*`
|
||||
- **Kotlin**: `build/`, `out/`, `.gradle/`, `.idea/`, `*.class`, `*.jar`, `*.iml`, `*.log`, `.env*`
|
||||
- **C++**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.so`, `*.a`, `*.exe`, `*.dll`, `.idea/`, `*.log`, `.env*`
|
||||
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `Makefile`, `config.log`, `.idea/`, `*.log`, `.env*`
|
||||
- **Swift**: `.build/`, `DerivedData/`, `*.swiftpm/`, `Packages/`
|
||||
- **R**: `.Rproj.user/`, `.Rhistory`, `.RData`, `.Ruserdata`, `*.Rproj`, `packrat/`, `renv/`
|
||||
- **Universal**: `.DS_Store`, `Thumbs.db`, `*.tmp`, `*.swp`, `.vscode/`, `.idea/`
|
||||
|
||||
**Tool-Specific Patterns**:
|
||||
- **Docker**: `node_modules/`, `.git/`, `Dockerfile*`, `.dockerignore`, `*.log*`, `.env*`, `coverage/`
|
||||
- **ESLint**: `node_modules/`, `dist/`, `build/`, `coverage/`, `*.min.js`
|
||||
- **Prettier**: `node_modules/`, `dist/`, `build/`, `coverage/`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
|
||||
- **Terraform**: `.terraform/`, `*.tfstate*`, `*.tfvars`, `.terraform.lock.hcl`
|
||||
- **Kubernetes/k8s**: `*.secret.yaml`, `secrets/`, `.kube/`, `kubeconfig*`, `*.key`, `*.crt`
|
||||
|
||||
5. Parse tasks.md structure and extract:
|
||||
- **Task phases**: Setup, Tests, Core, Integration, Polish
|
||||
- **Task dependencies**: Sequential vs parallel execution rules
|
||||
- **Task details**: ID, description, file paths, parallel markers [P]
|
||||
- **Execution flow**: Order and dependency requirements
|
||||
|
||||
5. Execute implementation following the task plan:
|
||||
6. Execute implementation following the task plan:
|
||||
- **Phase-by-phase execution**: Complete each phase before moving to the next
|
||||
- **Respect dependencies**: Run sequential tasks in order, parallel tasks [P] can run together
|
||||
- **Follow TDD approach**: Execute test tasks before their corresponding implementation tasks
|
||||
- **File-based coordination**: Tasks affecting the same files must run sequentially
|
||||
- **Validation checkpoints**: Verify each phase completion before proceeding
|
||||
|
||||
6. Implementation execution rules:
|
||||
7. Implementation execution rules:
|
||||
- **Setup first**: Initialize project structure, dependencies, configuration
|
||||
- **Tests before code**: If you need to write tests for contracts, entities, and integration scenarios
|
||||
- **Core development**: Implement models, services, CLI commands, endpoints
|
||||
- **Integration work**: Database connections, middleware, logging, external services
|
||||
- **Polish and validation**: Unit tests, performance optimization, documentation
|
||||
|
||||
7. Progress tracking and error handling:
|
||||
8. Progress tracking and error handling:
|
||||
- Report progress after each completed task
|
||||
- Halt execution if any non-parallel task fails
|
||||
- For parallel tasks [P], continue with successful tasks, report failed ones
|
||||
@@ -82,11 +128,11 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- Suggest next steps if implementation cannot proceed
|
||||
- **IMPORTANT** For completed tasks, make sure to mark the task off as [X] in the tasks file.
|
||||
|
||||
8. Completion validation:
|
||||
9. Completion validation:
|
||||
- Verify all required tasks are completed
|
||||
- Check that implemented features match the original specification
|
||||
- Validate that tests pass and coverage meets requirements
|
||||
- Confirm the implementation follows the technical plan
|
||||
- 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 `/tasks` first to regenerate the task list.
|
||||
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
|
||||
@@ -20,7 +28,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
|
||||
1. **Setup**: Run `{SCRIPT}` from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. 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").
|
||||
|
||||
2. **Load context**: Read FEATURE_SPEC and `.specify/memory/constitution.md`. Load IMPL_PLAN template (already copied).
|
||||
2. **Load context**: Read FEATURE_SPEC and `/memory/constitution.md`. Load IMPL_PLAN template (already copied).
|
||||
|
||||
3. **Execute plan workflow**: Follow the structure in IMPL_PLAN template to:
|
||||
- Fill Technical Context (mark unknowns as "NEEDS CLARIFICATION")
|
||||
@@ -43,7 +51,8 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- For each integration → patterns task
|
||||
|
||||
2. **Generate and dispatch research agents**:
|
||||
```
|
||||
|
||||
```text
|
||||
For each unknown in Technical Context:
|
||||
Task: "Research {unknown} for {feature context}"
|
||||
For each technology choice:
|
||||
|
||||
@@ -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}"
|
||||
@@ -19,11 +27,53 @@ The text the user typed after `/speckit.specify` in the triggering message **is*
|
||||
|
||||
Given that feature description, do this:
|
||||
|
||||
1. Run the script `{SCRIPT}` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
|
||||
**IMPORTANT** 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. 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").
|
||||
2. Load `templates/spec-template.md` to understand required sections.
|
||||
1. **Generate a concise short name** (2-4 words) for the branch:
|
||||
- Analyze the feature description and extract the most meaningful keywords
|
||||
- Create a 2-4 word short name that captures the essence of the feature
|
||||
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
|
||||
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
|
||||
- Keep it concise but descriptive enough to understand the feature at a glance
|
||||
- Examples:
|
||||
- "I want to add user authentication" → "user-auth"
|
||||
- "Implement OAuth2 integration for the API" → "oauth2-api-integration"
|
||||
- "Create a dashboard for analytics" → "analytics-dashboard"
|
||||
- "Fix payment processing timeout bug" → "fix-payment-timeout"
|
||||
|
||||
3. Follow this execution flow:
|
||||
2. **Check for existing branches before creating new one**:
|
||||
|
||||
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**:
|
||||
- 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
|
||||
- If no existing branches/directories found with this short-name, start with number 1
|
||||
- You must only ever run this script once per feature
|
||||
- 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")
|
||||
|
||||
3. Load `templates/spec-template.md` to understand required sections.
|
||||
|
||||
4. Follow this execution flow:
|
||||
|
||||
1. Parse user description from Input
|
||||
If empty: ERROR "No feature description provided"
|
||||
@@ -49,12 +99,12 @@ Given that feature description, do this:
|
||||
7. Identify Key Entities (if data involved)
|
||||
8. Return: SUCCESS (spec ready for planning)
|
||||
|
||||
4. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
|
||||
5. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
|
||||
|
||||
5. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria:
|
||||
6. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria:
|
||||
|
||||
a. **Create Spec Quality Checklist**: Generate a checklist file at `FEATURE_DIR/checklists/requirements.md` using the checklist template structure with these validation items:
|
||||
|
||||
|
||||
```markdown
|
||||
# Specification Quality Checklist: [FEATURE NAME]
|
||||
|
||||
@@ -91,26 +141,26 @@ Given that feature description, do this:
|
||||
|
||||
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
|
||||
```
|
||||
|
||||
|
||||
b. **Run Validation Check**: Review the spec against each checklist item:
|
||||
- For each item, determine if it passes or fails
|
||||
- Document specific issues found (quote relevant spec sections)
|
||||
|
||||
|
||||
c. **Handle Validation Results**:
|
||||
|
||||
|
||||
- **If all items pass**: Mark checklist complete and proceed to step 6
|
||||
|
||||
|
||||
- **If items fail (excluding [NEEDS CLARIFICATION])**:
|
||||
1. List the failing items and specific issues
|
||||
2. Update the spec to address each issue
|
||||
3. Re-run validation until all items pass (max 3 iterations)
|
||||
4. If still failing after 3 iterations, document remaining issues in checklist notes and warn user
|
||||
|
||||
|
||||
- **If [NEEDS CLARIFICATION] markers remain**:
|
||||
1. Extract all [NEEDS CLARIFICATION: ...] markers from the spec
|
||||
2. **LIMIT CHECK**: If more than 3 markers exist, keep only the 3 most critical (by scope/security/UX impact) and make informed guesses for the rest
|
||||
3. For each clarification needed (max 3), present options to user in this format:
|
||||
|
||||
|
||||
```markdown
|
||||
## Question [N]: [Topic]
|
||||
|
||||
@@ -129,7 +179,7 @@ Given that feature description, do this:
|
||||
|
||||
**Your choice**: _[Wait for user response]_
|
||||
```
|
||||
|
||||
|
||||
4. **CRITICAL - Table Formatting**: Ensure markdown tables are properly formatted:
|
||||
- Use consistent spacing with pipes aligned
|
||||
- Each cell should have spaces around content: `| Content |` not `|Content|`
|
||||
@@ -140,10 +190,10 @@ Given that feature description, do this:
|
||||
7. Wait for user to respond with their choices for all questions (e.g., "Q1: A, Q2: Custom - [details], Q3: B")
|
||||
8. Update the spec by replacing each [NEEDS CLARIFICATION] marker with the user's selected or provided answer
|
||||
9. Re-run validation after all clarifications are resolved
|
||||
|
||||
|
||||
d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
|
||||
|
||||
6. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
|
||||
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
|
||||
|
||||
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
|
||||
|
||||
@@ -178,7 +228,7 @@ When creating this spec from a user prompt:
|
||||
- Feature scope and boundaries (include/exclude specific use cases)
|
||||
- User types and permissions (if multiple conflicting interpretations possible)
|
||||
- Security/compliance requirements (when legally/financially significant)
|
||||
|
||||
|
||||
**Examples of reasonable defaults** (don't ask about these):
|
||||
|
||||
- Data retention: Industry-standard practices for the domain
|
||||
|
||||
@@ -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
|
||||
@@ -22,42 +31,25 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- **Optional**: data-model.md (entities), contracts/ (API endpoints), research.md (decisions), quickstart.md (test scenarios)
|
||||
- Note: Not all projects have all documents. Generate tasks based on what's available.
|
||||
|
||||
3. **Execute task generation workflow** (follow the template structure):
|
||||
3. **Execute task generation workflow**:
|
||||
- Load plan.md and extract tech stack, libraries, project structure
|
||||
- **Load spec.md and extract user stories with their priorities (P1, P2, P3, etc.)**
|
||||
- If data-model.md exists: Extract entities → map to user stories
|
||||
- If contracts/ exists: Each file → map endpoints to user stories
|
||||
- If research.md exists: Extract decisions → generate setup tasks
|
||||
- **Generate tasks ORGANIZED BY USER STORY**:
|
||||
- Setup tasks (shared infrastructure needed by all stories)
|
||||
- **Foundational tasks (prerequisites that must complete before ANY user story can start)**
|
||||
- For each user story (in priority order P1, P2, P3...):
|
||||
- Group all tasks needed to complete JUST that story
|
||||
- Include models, services, endpoints, UI components specific to that story
|
||||
- Mark which tasks are [P] parallelizable
|
||||
- If tests requested: Include tests specific to that story
|
||||
- Polish/Integration tasks (cross-cutting concerns)
|
||||
- **Tests are OPTIONAL**: Only generate test tasks if explicitly requested in the feature spec or user asks for TDD approach
|
||||
- Apply task rules:
|
||||
- Different files = mark [P] for parallel
|
||||
- Same file = sequential (no [P])
|
||||
- If tests requested: Tests before implementation (TDD order)
|
||||
- Number tasks sequentially (T001, T002...)
|
||||
- Load spec.md and extract user stories with their priorities (P1, P2, P3, etc.)
|
||||
- If data-model.md exists: Extract entities and map to user stories
|
||||
- If contracts/ exists: Map endpoints to user stories
|
||||
- If research.md exists: Extract decisions for setup tasks
|
||||
- Generate tasks organized by user story (see Task Generation Rules below)
|
||||
- Generate dependency graph showing user story completion order
|
||||
- Create parallel execution examples per user story
|
||||
- Validate task completeness (each user story has all needed tasks, independently testable)
|
||||
|
||||
4. **Generate tasks.md**: Use `.specify/templates/tasks-template.md` as structure, fill with:
|
||||
4. **Generate tasks.md**: Use `templates/tasks-template.md` as structure, fill with:
|
||||
- Correct feature name from plan.md
|
||||
- Phase 1: Setup tasks (project initialization)
|
||||
- Phase 2: Foundational tasks (blocking prerequisites for all user stories)
|
||||
- Phase 3+: One phase per user story (in priority order from spec.md)
|
||||
- Each phase includes: story goal, independent test criteria, tests (if requested), implementation tasks
|
||||
- Clear [Story] labels (US1, US2, US3...) for each task
|
||||
- [P] markers for parallelizable tasks within each story
|
||||
- Checkpoint markers after each story phase
|
||||
- Each phase includes: story goal, independent test criteria, tests (if requested), implementation tasks
|
||||
- Final Phase: Polish & cross-cutting concerns
|
||||
- Numbered tasks (T001, T002...) in execution order
|
||||
- All tasks must follow the strict checklist format (see Task Generation Rules below)
|
||||
- Clear file paths for each task
|
||||
- Dependencies section showing story completion order
|
||||
- Parallel execution examples per story
|
||||
@@ -69,6 +61,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
||||
- Parallel opportunities identified
|
||||
- Independent test criteria for each story
|
||||
- Suggested MVP scope (typically just User Story 1)
|
||||
- Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths)
|
||||
|
||||
Context for task generation: {ARGS}
|
||||
|
||||
@@ -76,10 +69,44 @@ The tasks.md should be immediately executable - each task must be specific enoug
|
||||
|
||||
## Task Generation Rules
|
||||
|
||||
**IMPORTANT**: Tests are optional. Only generate test tasks if the user explicitly requested testing or TDD approach in the feature specification.
|
||||
|
||||
**CRITICAL**: Tasks MUST be organized by user story to enable independent implementation and testing.
|
||||
|
||||
**Tests are OPTIONAL**: Only generate test tasks if explicitly requested in the feature specification or if user requests TDD approach.
|
||||
|
||||
### Checklist Format (REQUIRED)
|
||||
|
||||
Every task MUST strictly follow this format:
|
||||
|
||||
```text
|
||||
- [ ] [TaskID] [P?] [Story?] Description with file path
|
||||
```
|
||||
|
||||
**Format Components**:
|
||||
|
||||
1. **Checkbox**: ALWAYS start with `- [ ]` (markdown checkbox)
|
||||
2. **Task ID**: Sequential number (T001, T002, T003...) in execution order
|
||||
3. **[P] marker**: Include ONLY if task is parallelizable (different files, no dependencies on incomplete tasks)
|
||||
4. **[Story] label**: REQUIRED for user story phase tasks only
|
||||
- Format: [US1], [US2], [US3], etc. (maps to user stories from spec.md)
|
||||
- Setup phase: NO story label
|
||||
- Foundational phase: NO story label
|
||||
- User Story phases: MUST have story label
|
||||
- Polish phase: NO story label
|
||||
5. **Description**: Clear action with exact file path
|
||||
|
||||
**Examples**:
|
||||
|
||||
- ✅ CORRECT: `- [ ] T001 Create project structure per implementation plan`
|
||||
- ✅ CORRECT: `- [ ] T005 [P] Implement authentication middleware in src/middleware/auth.py`
|
||||
- ✅ CORRECT: `- [ ] T012 [P] [US1] Create User model in src/models/user.py`
|
||||
- ✅ CORRECT: `- [ ] T014 [US1] Implement UserService in src/services/user_service.py`
|
||||
- ❌ WRONG: `- [ ] Create User model` (missing ID and Story label)
|
||||
- ❌ WRONG: `T001 [US1] Create model` (missing checkbox)
|
||||
- ❌ WRONG: `- [ ] [US1] Create User model` (missing Task ID)
|
||||
- ❌ WRONG: `- [ ] T001 [US1] Create model` (missing file path)
|
||||
|
||||
### Task Organization
|
||||
|
||||
1. **From User Stories (spec.md)** - PRIMARY ORGANIZATION:
|
||||
- Each user story (P1, P2, P3...) gets its own phase
|
||||
- Map all related components to their story:
|
||||
@@ -88,28 +115,26 @@ The tasks.md should be immediately executable - each task must be specific enoug
|
||||
- Endpoints/UI needed for that story
|
||||
- If tests requested: Tests specific to that story
|
||||
- Mark story dependencies (most stories should be independent)
|
||||
|
||||
|
||||
2. **From Contracts**:
|
||||
- Map each contract/endpoint → to the user story it serves
|
||||
- If tests requested: Each contract → contract test task [P] before implementation in that story's phase
|
||||
|
||||
|
||||
3. **From Data Model**:
|
||||
- Map each entity → to the user story(ies) that need it
|
||||
- Map each entity to the user story(ies) that need it
|
||||
- If entity serves multiple stories: Put in earliest story or Setup phase
|
||||
- Relationships → service layer tasks in appropriate story phase
|
||||
|
||||
|
||||
4. **From Setup/Infrastructure**:
|
||||
- Shared infrastructure → Setup phase (Phase 1)
|
||||
- Foundational/blocking tasks → Foundational phase (Phase 2)
|
||||
- Examples: Database schema setup, authentication framework, core libraries, base configurations
|
||||
- These MUST complete before any user story can be implemented
|
||||
- Story-specific setup → within that story's phase
|
||||
|
||||
5. **Ordering**:
|
||||
- Phase 1: Setup (project initialization)
|
||||
- Phase 2: Foundational (blocking prerequisites - must complete before user stories)
|
||||
- Phase 3+: User Stories in priority order (P1, P2, P3...)
|
||||
- Within each story: Tests (if requested) → Models → Services → Endpoints → Integration
|
||||
- Final Phase: Polish & Cross-Cutting Concerns
|
||||
- Each user story phase should be a complete, independently testable increment
|
||||
### Phase Structure
|
||||
|
||||
- **Phase 1**: Setup (project initialization)
|
||||
- **Phase 2**: Foundational (blocking prerequisites - MUST complete before user stories)
|
||||
- **Phase 3+**: User Stories in priority order (P1, P2, P3...)
|
||||
- Within each story: Tests (if requested) → Models → Services → Endpoints → Integration
|
||||
- Each phase should be a complete, independently testable increment
|
||||
- **Final Phase**: Polish & Cross-Cutting Concerns
|
||||
|
||||
33
templates/commands/taskstoissues.md
Normal file
33
templates/commands/taskstoissues.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
description: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts.
|
||||
tools: ['github/github-mcp-server/issue_write']
|
||||
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
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> 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.
|
||||
|
||||
> [!CAUTION]
|
||||
> UNDER NO CIRCUMSTANCES EVER CREATE ISSUES IN REPOSITORIES THAT DO NOT MATCH THE REMOTE URL
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```
|
||||
```text
|
||||
specs/[###-feature]/
|
||||
├── plan.md # This file (/speckit.plan command output)
|
||||
├── research.md # Phase 0 output (/speckit.plan command)
|
||||
@@ -55,7 +55,7 @@ specs/[###-feature]/
|
||||
not include Option labels.
|
||||
-->
|
||||
|
||||
```
|
||||
```text
|
||||
# [REMOVE IF UNUSED] Option 1: Single project (DEFAULT)
|
||||
src/
|
||||
├── models/
|
||||
@@ -96,7 +96,7 @@ directories captured above]
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
*Fill ONLY if Constitution Check has violations that must be justified*
|
||||
> **Fill ONLY if Constitution Check has violations that must be justified**
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|-------------------------------------|
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
|
||||
description: "Task list template for feature implementation"
|
||||
---
|
||||
|
||||
@@ -12,11 +13,13 @@ description: "Task list template for feature implementation"
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Path Conventions
|
||||
|
||||
- **Single project**: `src/`, `tests/` at repository root
|
||||
- **Web app**: `backend/src/`, `frontend/src/`
|
||||
- **Mobile**: `api/src/`, `ios/src/` or `android/src/`
|
||||
@@ -78,7 +81,7 @@ Examples of foundational tasks (adjust based on your project):
|
||||
|
||||
### Tests for User Story 1 (OPTIONAL - only if tests requested) ⚠️
|
||||
|
||||
**NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
> **NOTE: Write these tests FIRST, ensure they FAIL before implementation**
|
||||
|
||||
- [ ] T010 [P] [US1] Contract test for [endpoint] in tests/contract/test_[name].py
|
||||
- [ ] T011 [P] [US1] Integration test for [user journey] in tests/integration/test_[name].py
|
||||
@@ -246,5 +249,3 @@ With multiple developers:
|
||||
- Commit after each task or logical group
|
||||
- Stop at any checkpoint to validate story independently
|
||||
- Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
".specify/scripts/bash/": true,
|
||||
".specify/scripts/ps/": true
|
||||
".specify/scripts/powershell/": true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user