mirror of
https://github.com/github/spec-kit.git
synced 2026-03-17 10:53:08 +00:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2656ee1df7 | ||
|
|
69ee7a836e | ||
|
|
c883952b43 | ||
|
|
b9c1a1c7bb | ||
|
|
46bc65b1ce | ||
|
|
017e1c4c2f | ||
|
|
7562664fd1 | ||
|
|
976c9981a4 | ||
|
|
d3fc056743 | ||
|
|
58ce653908 | ||
|
|
82f8a13f83 | ||
|
|
0f1cbd74fe | ||
|
|
ec60c5b2fe | ||
|
|
e56d37db8c | ||
|
|
33e853e9c9 | ||
|
|
929fab5d98 | ||
|
|
56095f06d2 | ||
|
|
2632a0f52d | ||
|
|
4ab91fbadf | ||
|
|
5c0bedb410 | ||
|
|
d92798d5b0 | ||
|
|
ee922cbde9 | ||
|
|
1df24f1953 | ||
|
|
3033834d64 | ||
|
|
4b00078907 | ||
|
|
2d72f85790 | ||
|
|
855ac838b8 | ||
|
|
a8ec87e3c2 | ||
|
|
9d6c05ad5b | ||
|
|
3ef12cae3e | ||
|
|
8618d0a53e | ||
|
|
71e6b4da4a | ||
|
|
ad74334a85 | ||
|
|
8c3982d65b | ||
|
|
13dec1de05 | ||
|
|
d0a112c60f | ||
|
|
c84756b7f3 | ||
|
|
524affca79 | ||
|
|
32c6e7f40c | ||
|
|
9cf33e81cc | ||
|
|
254e9bbdf0 | ||
|
|
6757c90dbd | ||
|
|
f6264d4ef4 | ||
|
|
dd8dbf6344 | ||
|
|
bf8fb125ad | ||
|
|
2b92ab444d | ||
|
|
abe1b7085c | ||
|
|
bfaca2c449 | ||
|
|
78ed453e38 | ||
|
|
658ab2a38c | ||
|
|
2c41d3627e | ||
|
|
b55d00beed | ||
|
|
525eae7f7e | ||
|
|
ce7bed4823 | ||
|
|
61b0637a6d | ||
|
|
56deda7be3 | ||
|
|
525cdc17ec | ||
|
|
607760e72f | ||
|
|
c7ecdfb998 | ||
|
|
f444ccba3a | ||
|
|
3040d33c31 | ||
|
|
6cc61025cb | ||
|
|
c1034f1d9d | ||
|
|
cee4f26fac | ||
|
|
6f523ede22 | ||
|
|
68d1d3a0fc | ||
|
|
07077d0fc2 | ||
|
|
aeed11f735 | ||
|
|
12405c01e1 | ||
|
|
fc3b98ea09 | ||
|
|
6150f1e317 | ||
|
|
6fca5d83b2 | ||
|
|
465acd9024 | ||
|
|
04fc3fd1ba | ||
|
|
24d76b5d92 | ||
|
|
0f7d04b12b | ||
|
|
9402ebd00a | ||
|
|
d410d188fc | ||
|
|
686c91f94e | ||
|
|
22036732d8 | ||
|
|
c78f8423f6 | ||
|
|
76cca34293 | ||
|
|
9a1e3037b0 | ||
|
|
f14a47ea7d | ||
|
|
36d97235ad | ||
|
|
4afbd87abb | ||
|
|
b562438df9 | ||
|
|
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 | ||
|
|
d4d3139d5f | ||
|
|
65f8787b48 | ||
|
|
9786e588b7 | ||
|
|
0ac76c8c7e | ||
|
|
115b4335d9 | ||
|
|
37e87c78a0 | ||
|
|
14a574a6a8 | ||
|
|
dbd1437aea | ||
|
|
317ae4dad9 | ||
|
|
8e9d25e9be | ||
|
|
c59be99dc4 | ||
|
|
15a5630047 | ||
|
|
8de5db7a3e | ||
|
|
09cf4f6cc4 |
@@ -50,8 +50,6 @@
|
|||||||
"kilocode.Kilo-Code",
|
"kilocode.Kilo-Code",
|
||||||
// Roo Code
|
// Roo Code
|
||||||
"RooVeterinaryInc.roo-cline",
|
"RooVeterinaryInc.roo-cline",
|
||||||
// Amazon Developer Q
|
|
||||||
"AmazonWebServices.amazon-q-vscode",
|
|
||||||
// Claude Code
|
// Claude Code
|
||||||
"anthropic.claude-code"
|
"anthropic.claude-code"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -51,32 +51,38 @@ echo -e "\n🤖 Installing OpenCode CLI..."
|
|||||||
run_command "npm install -g opencode-ai@latest"
|
run_command "npm install -g opencode-ai@latest"
|
||||||
echo "✅ Done"
|
echo "✅ Done"
|
||||||
|
|
||||||
echo -e "\n🤖 Installing Amazon Q CLI..."
|
echo -e "\n🤖 Installing Kiro CLI..."
|
||||||
# 👉🏾 https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-verify-download.html
|
# https://kiro.dev/docs/cli/
|
||||||
|
KIRO_INSTALLER_URL="https://kiro.dev/install.sh"
|
||||||
|
KIRO_INSTALLER_SHA256="7487a65cf310b7fb59b357c4b5e6e3f3259d383f4394ecedb39acf70f307cffb"
|
||||||
|
KIRO_INSTALLER_PATH="$(mktemp)"
|
||||||
|
|
||||||
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'"
|
cleanup_kiro_installer() {
|
||||||
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'"
|
rm -f "$KIRO_INSTALLER_PATH"
|
||||||
cat > amazonq-public-key.asc << 'EOF'
|
}
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
trap cleanup_kiro_installer EXIT
|
||||||
|
|
||||||
mDMEZig60RYJKwYBBAHaRw8BAQdAy/+G05U5/EOA72WlcD4WkYn5SInri8pc4Z6D
|
run_command "curl -fsSL \"$KIRO_INSTALLER_URL\" -o \"$KIRO_INSTALLER_PATH\""
|
||||||
BKNNGOm0JEFtYXpvbiBRIENMSSBUZWFtIDxxLWNsaUBhbWF6b24uY29tPoiZBBMW
|
run_command "echo \"$KIRO_INSTALLER_SHA256 $KIRO_INSTALLER_PATH\" | sha256sum -c -"
|
||||||
CgBBFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcFAmYoOtECGwMFCQPCZwAFCwkIBwIC
|
|
||||||
IgIGFQoJCAsCBBYCAwECHgcCF4AACgkQUNx6jcJMVmef5QD/QWWEGG/cOnbDnp68
|
run_command "bash \"$KIRO_INSTALLER_PATH\""
|
||||||
SJXuFkwiNwlH2rPw9ZRIQMnfAS0A/0V6ZsGB4kOylBfc7CNfzRFGtovdBBgHqA6P
|
|
||||||
zQ/PNscGuDgEZig60RIKKwYBBAGXVQEFAQEHQC4qleONMBCq3+wJwbZSr0vbuRba
|
kiro_binary=""
|
||||||
D1xr4wUPn4Avn4AnAwEIB4h+BBgWCgAmFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcF
|
if command -v kiro-cli >/dev/null 2>&1; then
|
||||||
AmYoOtECGwwFCQPCZwAACgkQUNx6jcJMVmchMgEA6l3RveCM0YHAGQaSFMkguoAo
|
kiro_binary="kiro-cli"
|
||||||
vK6FgOkDawgP0NPIP2oA/jIAO4gsAntuQgMOsPunEdDeji2t+AhV02+DQIsXZpoB
|
elif command -v kiro >/dev/null 2>&1; then
|
||||||
=f8yY
|
kiro_binary="kiro"
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
else
|
||||||
EOF
|
echo -e "\033[0;31m[ERROR] Kiro CLI installation did not create 'kiro-cli' or 'kiro' in PATH.\033[0m" >&2
|
||||||
run_command "gpg --batch --import amazonq-public-key.asc"
|
exit 1
|
||||||
run_command "gpg --verify q.zip.sig q.zip"
|
fi
|
||||||
run_command "unzip -q q.zip"
|
|
||||||
run_command "chmod +x ./q/install.sh"
|
run_command "$kiro_binary --help > /dev/null"
|
||||||
run_command "./q/install.sh --no-confirm"
|
echo "✅ Done"
|
||||||
run_command "rm -rf ./q q.zip q.zip.sig amazonq-public-key.asc"
|
|
||||||
|
echo -e "\n🤖 Installing Kimi CLI..."
|
||||||
|
# https://code.kimi.com
|
||||||
|
run_command "pipx install kimi-cli"
|
||||||
echo "✅ Done"
|
echo "✅ Done"
|
||||||
|
|
||||||
echo -e "\n🤖 Installing CodeBuddy CLI..."
|
echo -e "\n🤖 Installing CodeBuddy CLI..."
|
||||||
|
|||||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1,3 +1,3 @@
|
|||||||
# Global code owner
|
# Global code owner
|
||||||
* @localden
|
* @mnriem
|
||||||
|
|
||||||
|
|||||||
141
.github/ISSUE_TEMPLATE/agent_request.yml
vendored
Normal file
141
.github/ISSUE_TEMPLATE/agent_request.yml
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
name: Agent Request
|
||||||
|
description: Request support for a new AI agent/assistant in Spec Kit
|
||||||
|
title: "[Agent]: Add support for "
|
||||||
|
labels: ["agent-request", "enhancement", "needs-triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for requesting a new agent! Before submitting, please check if the agent is already supported.
|
||||||
|
|
||||||
|
**Currently supported agents**: Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy, Qoder CLI, Kiro CLI, Amp, SHAI, IBM Bob, Antigravity
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: agent-name
|
||||||
|
attributes:
|
||||||
|
label: Agent Name
|
||||||
|
description: What is the name of the AI agent/assistant?
|
||||||
|
placeholder: "e.g., SuperCoder AI"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: website
|
||||||
|
attributes:
|
||||||
|
label: Official Website
|
||||||
|
description: Link to the agent's official website or documentation
|
||||||
|
placeholder: "https://..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: agent-type
|
||||||
|
attributes:
|
||||||
|
label: Agent Type
|
||||||
|
description: How is the agent accessed?
|
||||||
|
options:
|
||||||
|
- CLI tool (command-line interface)
|
||||||
|
- IDE extension/plugin
|
||||||
|
- Both CLI and IDE
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: cli-command
|
||||||
|
attributes:
|
||||||
|
label: CLI Command (if applicable)
|
||||||
|
description: What command is used to invoke the agent from terminal?
|
||||||
|
placeholder: "e.g., supercode, ai-assistant"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Installation Method
|
||||||
|
description: How is the agent installed?
|
||||||
|
placeholder: "e.g., npm install -g supercode, pip install supercode, IDE marketplace"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: command-structure
|
||||||
|
attributes:
|
||||||
|
label: Command/Workflow Structure
|
||||||
|
description: How does the agent define custom commands or workflows?
|
||||||
|
placeholder: |
|
||||||
|
- Command file format (Markdown, YAML, TOML, etc.)
|
||||||
|
- Directory location (e.g., .supercode/commands/)
|
||||||
|
- Example command file structure
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: argument-pattern
|
||||||
|
attributes:
|
||||||
|
label: Argument Passing Pattern
|
||||||
|
description: How does the agent handle arguments in commands?
|
||||||
|
placeholder: |
|
||||||
|
e.g., Uses {{args}}, $ARGUMENTS, %ARGS%, or other placeholder format
|
||||||
|
Example: "Run test suite with {{args}}"
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: popularity
|
||||||
|
attributes:
|
||||||
|
label: Popularity/Usage
|
||||||
|
description: How widely is this agent used?
|
||||||
|
options:
|
||||||
|
- Widely used (thousands+ of users)
|
||||||
|
- Growing adoption (hundreds of users)
|
||||||
|
- New/emerging (less than 100 users)
|
||||||
|
- Unknown
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: documentation
|
||||||
|
attributes:
|
||||||
|
label: Documentation Links
|
||||||
|
description: Links to relevant documentation for custom commands/workflows
|
||||||
|
placeholder: |
|
||||||
|
- Command documentation: https://...
|
||||||
|
- API/CLI reference: https://...
|
||||||
|
- Examples: https://...
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: use-case
|
||||||
|
attributes:
|
||||||
|
label: Use Case
|
||||||
|
description: Why do you want this agent supported in Spec Kit?
|
||||||
|
placeholder: Explain your workflow and how this agent fits into your development process
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: example-command
|
||||||
|
attributes:
|
||||||
|
label: Example Command File
|
||||||
|
description: If possible, provide an example of a command file for this agent
|
||||||
|
render: markdown
|
||||||
|
placeholder: |
|
||||||
|
```toml
|
||||||
|
description = "Example command"
|
||||||
|
prompt = "Do something with {{args}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: contribution
|
||||||
|
attributes:
|
||||||
|
label: Contribution
|
||||||
|
description: Are you willing to help implement support for this agent?
|
||||||
|
options:
|
||||||
|
- label: I can help test the integration
|
||||||
|
- label: I can provide example command files
|
||||||
|
- label: I can help with documentation
|
||||||
|
- label: I can submit a pull request for the integration
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Any other relevant information about this agent
|
||||||
|
placeholder: Screenshots, community links, comparison to existing agents, etc.
|
||||||
118
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
118
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Report a bug or unexpected behavior in Specify CLI or Spec Kit
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["bug", "needs-triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to report a bug! Please fill out the sections below to help us diagnose and fix the issue.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Bug Description
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
placeholder: What went wrong?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: reproduce
|
||||||
|
attributes:
|
||||||
|
label: Steps to Reproduce
|
||||||
|
description: Steps to reproduce the behavior
|
||||||
|
placeholder: |
|
||||||
|
1. Run command '...'
|
||||||
|
2. Execute script '...'
|
||||||
|
3. See error
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: What did you expect to happen?
|
||||||
|
placeholder: Describe the expected outcome
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual Behavior
|
||||||
|
description: What actually happened?
|
||||||
|
placeholder: Describe what happened instead
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Specify CLI Version
|
||||||
|
description: "Run `specify version` or `pip show spec-kit`"
|
||||||
|
placeholder: "e.g., 1.3.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: ai-agent
|
||||||
|
attributes:
|
||||||
|
label: AI Agent
|
||||||
|
description: Which AI agent are you using?
|
||||||
|
options:
|
||||||
|
- Claude Code
|
||||||
|
- Gemini CLI
|
||||||
|
- GitHub Copilot
|
||||||
|
- Cursor
|
||||||
|
- Qwen Code
|
||||||
|
- opencode
|
||||||
|
- Codex CLI
|
||||||
|
- Windsurf
|
||||||
|
- Kilo Code
|
||||||
|
- Auggie CLI
|
||||||
|
- Roo Code
|
||||||
|
- CodeBuddy
|
||||||
|
- Qoder CLI
|
||||||
|
- Kiro CLI
|
||||||
|
- Amp
|
||||||
|
- SHAI
|
||||||
|
- IBM Bob
|
||||||
|
- Antigravity
|
||||||
|
- Not applicable
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: Your operating system and version
|
||||||
|
placeholder: "e.g., macOS 14.2, Ubuntu 22.04, Windows 11"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: python
|
||||||
|
attributes:
|
||||||
|
label: Python Version
|
||||||
|
description: "Run `python --version` or `python3 --version`"
|
||||||
|
placeholder: "e.g., Python 3.11.5"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Error Logs
|
||||||
|
description: Please paste any relevant error messages or logs
|
||||||
|
render: shell
|
||||||
|
placeholder: Paste error output here
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context about the problem
|
||||||
|
placeholder: Screenshots, related issues, workarounds attempted, etc.
|
||||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: 💬 General Discussion
|
||||||
|
url: https://github.com/github/spec-kit/discussions
|
||||||
|
about: Ask questions, share ideas, or discuss Spec-Driven Development
|
||||||
|
- name: 📖 Documentation
|
||||||
|
url: https://github.com/github/spec-kit/blob/main/README.md
|
||||||
|
about: Read the Spec Kit documentation and guides
|
||||||
|
- name: 🛠️ Extension Development Guide
|
||||||
|
url: https://github.com/manfredseee/spec-kit/blob/main/extensions/EXTENSION-DEVELOPMENT-GUIDE.md
|
||||||
|
about: Learn how to develop and publish Spec Kit extensions
|
||||||
|
- name: 🤝 Contributing Guide
|
||||||
|
url: https://github.com/github/spec-kit/blob/main/CONTRIBUTING.md
|
||||||
|
about: Learn how to contribute to Spec Kit
|
||||||
|
- name: 🔒 Security Issues
|
||||||
|
url: https://github.com/github/spec-kit/blob/main/SECURITY.md
|
||||||
|
about: Report security vulnerabilities privately
|
||||||
280
.github/ISSUE_TEMPLATE/extension_submission.yml
vendored
Normal file
280
.github/ISSUE_TEMPLATE/extension_submission.yml
vendored
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
name: Extension Submission
|
||||||
|
description: Submit your extension to the Spec Kit catalog
|
||||||
|
title: "[Extension]: Add "
|
||||||
|
labels: ["extension-submission", "enhancement", "needs-triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for contributing an extension! This template helps you submit your extension to the community catalog.
|
||||||
|
|
||||||
|
**Before submitting:**
|
||||||
|
- Review the [Extension Publishing Guide](https://github.com/github/spec-kit/blob/main/extensions/EXTENSION-PUBLISHING-GUIDE.md)
|
||||||
|
- Ensure your extension has a valid `extension.yml` manifest
|
||||||
|
- Create a GitHub release with a version tag (e.g., v1.0.0)
|
||||||
|
- Test installation: `specify extension add --from <your-release-url>`
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: extension-id
|
||||||
|
attributes:
|
||||||
|
label: Extension ID
|
||||||
|
description: Unique extension identifier (lowercase with hyphens only)
|
||||||
|
placeholder: "e.g., jira-integration"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: extension-name
|
||||||
|
attributes:
|
||||||
|
label: Extension Name
|
||||||
|
description: Human-readable extension name
|
||||||
|
placeholder: "e.g., Jira Integration"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: Semantic version number
|
||||||
|
placeholder: "e.g., 1.0.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: Brief description of what your extension does (under 200 characters)
|
||||||
|
placeholder: Integrates Jira issue tracking with Spec Kit workflows for seamless task management
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: author
|
||||||
|
attributes:
|
||||||
|
label: Author
|
||||||
|
description: Your name or organization
|
||||||
|
placeholder: "e.g., John Doe or Acme Corp"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: repository
|
||||||
|
attributes:
|
||||||
|
label: Repository URL
|
||||||
|
description: GitHub repository URL for your extension
|
||||||
|
placeholder: "https://github.com/your-org/spec-kit-your-extension"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: download-url
|
||||||
|
attributes:
|
||||||
|
label: Download URL
|
||||||
|
description: URL to the GitHub release archive (e.g., v1.0.0.zip)
|
||||||
|
placeholder: "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: license
|
||||||
|
attributes:
|
||||||
|
label: License
|
||||||
|
description: Open source license type
|
||||||
|
placeholder: "e.g., MIT, Apache-2.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: homepage
|
||||||
|
attributes:
|
||||||
|
label: Homepage (optional)
|
||||||
|
description: Link to extension homepage or documentation site
|
||||||
|
placeholder: "https://..."
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: documentation
|
||||||
|
attributes:
|
||||||
|
label: Documentation URL (optional)
|
||||||
|
description: Link to detailed documentation
|
||||||
|
placeholder: "https://github.com/your-org/spec-kit-your-extension/blob/main/docs/"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: changelog
|
||||||
|
attributes:
|
||||||
|
label: Changelog URL (optional)
|
||||||
|
description: Link to changelog file
|
||||||
|
placeholder: "https://github.com/your-org/spec-kit-your-extension/blob/main/CHANGELOG.md"
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: speckit-version
|
||||||
|
attributes:
|
||||||
|
label: Required Spec Kit Version
|
||||||
|
description: Minimum Spec Kit version required
|
||||||
|
placeholder: "e.g., >=0.1.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: required-tools
|
||||||
|
attributes:
|
||||||
|
label: Required Tools (optional)
|
||||||
|
description: List any external tools or dependencies required
|
||||||
|
placeholder: |
|
||||||
|
- jira-cli (>=1.0.0) - required
|
||||||
|
- python (>=3.8) - optional
|
||||||
|
render: markdown
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: commands-count
|
||||||
|
attributes:
|
||||||
|
label: Number of Commands
|
||||||
|
description: How many commands does your extension provide?
|
||||||
|
placeholder: "e.g., 3"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: hooks-count
|
||||||
|
attributes:
|
||||||
|
label: Number of Hooks (optional)
|
||||||
|
description: How many hooks does your extension provide?
|
||||||
|
placeholder: "e.g., 0"
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: tags
|
||||||
|
attributes:
|
||||||
|
label: Tags
|
||||||
|
description: 2-5 relevant tags (lowercase, separated by commas)
|
||||||
|
placeholder: "issue-tracking, jira, atlassian, automation"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: features
|
||||||
|
attributes:
|
||||||
|
label: Key Features
|
||||||
|
description: List the main features and capabilities of your extension
|
||||||
|
placeholder: |
|
||||||
|
- Create Jira issues from specs
|
||||||
|
- Sync task status with Jira
|
||||||
|
- Link specs to existing issues
|
||||||
|
- Generate Jira reports
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: testing
|
||||||
|
attributes:
|
||||||
|
label: Testing Checklist
|
||||||
|
description: Confirm that your extension has been tested
|
||||||
|
options:
|
||||||
|
- label: Extension installs successfully via download URL
|
||||||
|
required: true
|
||||||
|
- label: All commands execute without errors
|
||||||
|
required: true
|
||||||
|
- label: Documentation is complete and accurate
|
||||||
|
required: true
|
||||||
|
- label: No security vulnerabilities identified
|
||||||
|
required: true
|
||||||
|
- label: Tested on at least one real project
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: requirements
|
||||||
|
attributes:
|
||||||
|
label: Submission Requirements
|
||||||
|
description: Verify your extension meets all requirements
|
||||||
|
options:
|
||||||
|
- label: Valid `extension.yml` manifest included
|
||||||
|
required: true
|
||||||
|
- label: README.md with installation and usage instructions
|
||||||
|
required: true
|
||||||
|
- label: LICENSE file included
|
||||||
|
required: true
|
||||||
|
- label: GitHub release created with version tag
|
||||||
|
required: true
|
||||||
|
- label: All command files exist and are properly formatted
|
||||||
|
required: true
|
||||||
|
- label: Extension ID follows naming conventions (lowercase-with-hyphens)
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: testing-details
|
||||||
|
attributes:
|
||||||
|
label: Testing Details
|
||||||
|
description: Describe how you tested your extension
|
||||||
|
placeholder: |
|
||||||
|
**Tested on:**
|
||||||
|
- macOS 14.0 with Spec Kit v0.1.0
|
||||||
|
- Linux Ubuntu 22.04 with Spec Kit v0.1.0
|
||||||
|
|
||||||
|
**Test project:** [Link or description]
|
||||||
|
|
||||||
|
**Test scenarios:**
|
||||||
|
1. Installed extension
|
||||||
|
2. Configured settings
|
||||||
|
3. Ran all commands
|
||||||
|
4. Verified outputs
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: example-usage
|
||||||
|
attributes:
|
||||||
|
label: Example Usage
|
||||||
|
description: Provide a simple example of using your extension
|
||||||
|
render: markdown
|
||||||
|
placeholder: |
|
||||||
|
```bash
|
||||||
|
# Install extension
|
||||||
|
specify extension add --from https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
|
||||||
|
|
||||||
|
# Use a command
|
||||||
|
/speckit.your-extension.command-name arg1 arg2
|
||||||
|
```
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: catalog-entry
|
||||||
|
attributes:
|
||||||
|
label: Proposed Catalog Entry
|
||||||
|
description: Provide the JSON entry for catalog.json (helps reviewers)
|
||||||
|
render: json
|
||||||
|
placeholder: |
|
||||||
|
{
|
||||||
|
"your-extension": {
|
||||||
|
"name": "Your Extension",
|
||||||
|
"id": "your-extension",
|
||||||
|
"description": "Brief description",
|
||||||
|
"author": "Your Name",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/your-org/spec-kit-your-extension",
|
||||||
|
"homepage": "https://github.com/your-org/spec-kit-your-extension",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 3
|
||||||
|
},
|
||||||
|
"tags": ["category", "tool"],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-02-20T00:00:00Z",
|
||||||
|
"updated_at": "2026-02-20T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Any other information that would help reviewers
|
||||||
|
placeholder: Screenshots, demo videos, links to related projects, etc.
|
||||||
104
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
104
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: Suggest a new feature or enhancement for Specify CLI or Spec Kit
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: ["enhancement", "needs-triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for suggesting a feature! Please provide details below to help us understand and evaluate your request.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: Problem Statement
|
||||||
|
description: Is your feature request related to a problem? Please describe.
|
||||||
|
placeholder: "I'm frustrated when..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Proposed Solution
|
||||||
|
description: Describe the solution you'd like
|
||||||
|
placeholder: What would you like to happen?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives Considered
|
||||||
|
description: Have you considered any alternative solutions or workarounds?
|
||||||
|
placeholder: What other approaches might work?
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: component
|
||||||
|
attributes:
|
||||||
|
label: Component
|
||||||
|
description: Which component does this feature relate to?
|
||||||
|
options:
|
||||||
|
- Specify CLI (initialization, commands)
|
||||||
|
- Spec templates (BDD, Testing Strategy, etc.)
|
||||||
|
- Agent integrations (command files, workflows)
|
||||||
|
- Scripts (Bash/PowerShell utilities)
|
||||||
|
- Documentation
|
||||||
|
- CI/CD workflows
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: ai-agent
|
||||||
|
attributes:
|
||||||
|
label: AI Agent (if applicable)
|
||||||
|
description: Does this feature relate to a specific AI agent?
|
||||||
|
options:
|
||||||
|
- All agents
|
||||||
|
- Claude Code
|
||||||
|
- Gemini CLI
|
||||||
|
- GitHub Copilot
|
||||||
|
- Cursor
|
||||||
|
- Qwen Code
|
||||||
|
- opencode
|
||||||
|
- Codex CLI
|
||||||
|
- Windsurf
|
||||||
|
- Kilo Code
|
||||||
|
- Auggie CLI
|
||||||
|
- Roo Code
|
||||||
|
- CodeBuddy
|
||||||
|
- Qoder CLI
|
||||||
|
- Kiro CLI
|
||||||
|
- Amp
|
||||||
|
- SHAI
|
||||||
|
- IBM Bob
|
||||||
|
- Antigravity
|
||||||
|
- Not applicable
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: use-cases
|
||||||
|
attributes:
|
||||||
|
label: Use Cases
|
||||||
|
description: Describe specific use cases where this feature would be valuable
|
||||||
|
placeholder: |
|
||||||
|
1. When working on large projects...
|
||||||
|
2. During spec review...
|
||||||
|
3. When integrating with CI/CD...
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: acceptance
|
||||||
|
attributes:
|
||||||
|
label: Acceptance Criteria
|
||||||
|
description: How would you know this feature is complete and working?
|
||||||
|
placeholder: |
|
||||||
|
- [ ] Feature does X
|
||||||
|
- [ ] Documentation is updated
|
||||||
|
- [ ] Works with all supported agents
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context, screenshots, or examples
|
||||||
|
placeholder: Links to similar features, mockups, related discussions, etc.
|
||||||
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- What does this PR do? Why is it needed? -->
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
<!-- How did you test your changes? -->
|
||||||
|
|
||||||
|
- [ ] Tested locally with `uv run specify --help`
|
||||||
|
- [ ] Ran existing tests with `uv sync && uv run pytest`
|
||||||
|
- [ ] Tested with a sample project (if applicable)
|
||||||
|
|
||||||
|
## AI Disclosure
|
||||||
|
|
||||||
|
<!-- Per our Contributing guidelines, AI assistance must be disclosed. -->
|
||||||
|
<!-- See: https://github.com/github/spec-kit/blob/main/CONTRIBUTING.md#ai-contributions-in-spec-kit -->
|
||||||
|
|
||||||
|
- [ ] I **did not** use AI assistance for this contribution
|
||||||
|
- [ ] I **did** use AI assistance (describe below)
|
||||||
|
|
||||||
|
<!-- If you used AI, briefly describe how (e.g., "Code generated by Copilot", "Consulted ChatGPT for approach"): -->
|
||||||
|
|
||||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "pip"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
191
.github/workflows/RELEASE-PROCESS.md
vendored
Normal file
191
.github/workflows/RELEASE-PROCESS.md
vendored
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
# Release Process
|
||||||
|
|
||||||
|
This document describes the automated release process for Spec Kit.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The release process is split into two workflows to ensure version consistency:
|
||||||
|
|
||||||
|
1. **Release Trigger Workflow** (`release-trigger.yml`) - Manages versioning and triggers release
|
||||||
|
2. **Release Workflow** (`release.yml`) - Builds and publishes artifacts
|
||||||
|
|
||||||
|
This separation ensures that git tags always point to commits with the correct version in `pyproject.toml`.
|
||||||
|
|
||||||
|
## Before Creating a Release
|
||||||
|
|
||||||
|
**Important**: Write clear, descriptive commit messages!
|
||||||
|
|
||||||
|
### How CHANGELOG.md Works
|
||||||
|
|
||||||
|
The CHANGELOG is **automatically generated** from your git commit messages:
|
||||||
|
|
||||||
|
1. **During Development**: Write clear, descriptive commit messages:
|
||||||
|
```bash
|
||||||
|
git commit -m "feat: Add new authentication feature"
|
||||||
|
git commit -m "fix: Resolve timeout issue in API client (#123)"
|
||||||
|
git commit -m "docs: Update installation instructions"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **When Releasing**: The release trigger workflow automatically:
|
||||||
|
- Finds all commits since the last release tag
|
||||||
|
- Formats them as changelog entries
|
||||||
|
- Inserts them into CHANGELOG.md
|
||||||
|
- Commits the updated changelog before creating the new tag
|
||||||
|
|
||||||
|
### Commit Message Best Practices
|
||||||
|
|
||||||
|
Good commit messages make good changelogs:
|
||||||
|
- **Be descriptive**: "Add user authentication" not "Update files"
|
||||||
|
- **Reference issues/PRs**: Include `(#123)` for automated linking
|
||||||
|
- **Use conventional commits** (optional): `feat:`, `fix:`, `docs:`, `chore:`
|
||||||
|
- **Keep it concise**: One line is ideal, details go in commit body
|
||||||
|
|
||||||
|
**Example commits that become good changelog entries:**
|
||||||
|
```
|
||||||
|
fix: prepend YAML frontmatter to Cursor .mdc files (#1699)
|
||||||
|
feat: add generic agent support with customizable command directories (#1639)
|
||||||
|
docs: document dual-catalog system for extensions (#1689)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a Release
|
||||||
|
|
||||||
|
### Option 1: Auto-Increment (Recommended for patches)
|
||||||
|
|
||||||
|
1. Go to **Actions** → **Release Trigger**
|
||||||
|
2. Click **Run workflow**
|
||||||
|
3. Leave the version field **empty**
|
||||||
|
4. Click **Run workflow**
|
||||||
|
|
||||||
|
The workflow will:
|
||||||
|
- Auto-increment the patch version (e.g., `0.1.10` → `0.1.11`)
|
||||||
|
- Update `pyproject.toml`
|
||||||
|
- Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag
|
||||||
|
- Commit changes to a `chore/release-vX.Y.Z` branch
|
||||||
|
- Create and push the git tag from that branch
|
||||||
|
- Open a PR to merge the version bump into `main`
|
||||||
|
- Trigger the release workflow automatically via the tag push
|
||||||
|
|
||||||
|
### Option 2: Manual Version (For major/minor bumps)
|
||||||
|
|
||||||
|
1. Go to **Actions** → **Release Trigger**
|
||||||
|
2. Click **Run workflow**
|
||||||
|
3. Enter the desired version (e.g., `0.2.0` or `v0.2.0`)
|
||||||
|
4. Click **Run workflow**
|
||||||
|
|
||||||
|
The workflow will:
|
||||||
|
- Use your specified version
|
||||||
|
- Update `pyproject.toml`
|
||||||
|
- Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag
|
||||||
|
- Commit changes to a `chore/release-vX.Y.Z` branch
|
||||||
|
- Create and push the git tag from that branch
|
||||||
|
- Open a PR to merge the version bump into `main`
|
||||||
|
- Trigger the release workflow automatically via the tag push
|
||||||
|
|
||||||
|
## What Happens Next
|
||||||
|
|
||||||
|
Once the release trigger workflow completes:
|
||||||
|
|
||||||
|
1. A `chore/release-vX.Y.Z` branch is pushed with the version bump commit
|
||||||
|
2. The git tag is pushed, pointing to that commit
|
||||||
|
3. The **Release Workflow** is automatically triggered by the tag push
|
||||||
|
4. Release artifacts are built for all supported agents
|
||||||
|
5. A GitHub Release is created with all assets
|
||||||
|
6. A PR is opened to merge the version bump branch into `main`
|
||||||
|
|
||||||
|
> **Note**: Merge the auto-opened PR after the release is published to keep `main` in sync.
|
||||||
|
|
||||||
|
## Workflow Details
|
||||||
|
|
||||||
|
### Release Trigger Workflow
|
||||||
|
|
||||||
|
**File**: `.github/workflows/release-trigger.yml`
|
||||||
|
|
||||||
|
**Trigger**: Manual (`workflow_dispatch`)
|
||||||
|
|
||||||
|
**Permissions Required**: `contents: write`
|
||||||
|
|
||||||
|
**Steps**:
|
||||||
|
1. Checkout repository
|
||||||
|
2. Determine version (manual or auto-increment)
|
||||||
|
3. Check if tag already exists (prevents duplicates)
|
||||||
|
4. Create `chore/release-vX.Y.Z` branch
|
||||||
|
5. Update `pyproject.toml`
|
||||||
|
6. Update `CHANGELOG.md` from git commits
|
||||||
|
7. Commit changes
|
||||||
|
8. Push branch and tag
|
||||||
|
9. Open PR to merge version bump into `main`
|
||||||
|
|
||||||
|
### Release Workflow
|
||||||
|
|
||||||
|
**File**: `.github/workflows/release.yml`
|
||||||
|
|
||||||
|
**Trigger**: Tag push (`v*`)
|
||||||
|
|
||||||
|
**Permissions Required**: `contents: write`
|
||||||
|
|
||||||
|
**Steps**:
|
||||||
|
1. Checkout repository at tag
|
||||||
|
2. Extract version from tag name
|
||||||
|
3. Check if release already exists
|
||||||
|
4. Build release package variants (all agents × shell/powershell)
|
||||||
|
5. Generate release notes from commits
|
||||||
|
6. Create GitHub Release with all assets
|
||||||
|
|
||||||
|
## Version Constraints
|
||||||
|
|
||||||
|
- Tags must follow format: `v{MAJOR}.{MINOR}.{PATCH}`
|
||||||
|
- Example valid versions: `v0.1.11`, `v0.2.0`, `v1.0.0`
|
||||||
|
- Auto-increment only bumps patch version
|
||||||
|
- Cannot create duplicate tags (workflow will fail)
|
||||||
|
|
||||||
|
## Benefits of This Approach
|
||||||
|
|
||||||
|
✅ **Version Consistency**: Git tags point to commits with matching `pyproject.toml` version
|
||||||
|
|
||||||
|
✅ **Single Source of Truth**: Version set once, used everywhere
|
||||||
|
|
||||||
|
✅ **Prevents Drift**: No more manual version synchronization needed
|
||||||
|
|
||||||
|
✅ **Clean Separation**: Versioning logic separate from artifact building
|
||||||
|
|
||||||
|
✅ **Flexibility**: Supports both auto-increment and manual versioning
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### No Commits Since Last Release
|
||||||
|
|
||||||
|
If you run the release trigger workflow when there are no new commits since the last tag:
|
||||||
|
- The workflow will still succeed
|
||||||
|
- The CHANGELOG will show "- Initial release" if it's the first release
|
||||||
|
- Or it will be empty if there are no commits
|
||||||
|
- Consider adding meaningful commits before releasing
|
||||||
|
|
||||||
|
**Best Practice**: Use descriptive commit messages - they become your changelog!
|
||||||
|
|
||||||
|
### Tag Already Exists
|
||||||
|
|
||||||
|
If you see "Error: Tag vX.Y.Z already exists!", you need to:
|
||||||
|
- Choose a different version number, or
|
||||||
|
- Delete the existing tag if it was created in error
|
||||||
|
|
||||||
|
### Release Workflow Didn't Trigger
|
||||||
|
|
||||||
|
Check that:
|
||||||
|
- The release trigger workflow completed successfully
|
||||||
|
- The tag was pushed (check repository tags)
|
||||||
|
- The release workflow is enabled in Actions settings
|
||||||
|
|
||||||
|
### Version Mismatch
|
||||||
|
|
||||||
|
If `pyproject.toml` doesn't match the latest tag:
|
||||||
|
- Run the release trigger workflow to sync versions
|
||||||
|
- Or manually update `pyproject.toml` and push changes before running the release trigger
|
||||||
|
|
||||||
|
## Legacy Behavior (Pre-v0.1.10)
|
||||||
|
|
||||||
|
Before this change, the release workflow:
|
||||||
|
- Created tags automatically on main branch pushes
|
||||||
|
- Updated `pyproject.toml` AFTER creating the tag
|
||||||
|
- Resulted in tags pointing to commits with outdated versions
|
||||||
|
|
||||||
|
This has been fixed in v0.1.10+.
|
||||||
32
.github/workflows/codeql.yml
vendored
Normal file
32
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
contents: read
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'actions', 'python' ]
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v4
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v4
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
||||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Fetch all history for git info
|
fetch-depth: 0 # Fetch all history for git info
|
||||||
|
|
||||||
|
|||||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -1,4 +1,6 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -10,9 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Run markdownlint-cli2
|
- name: Run markdownlint-cli2
|
||||||
uses: DavidAnson/markdownlint-cli2-action@v19
|
uses: DavidAnson/markdownlint-cli2-action@v19
|
||||||
with:
|
with:
|
||||||
globs: '**/*.md'
|
globs: |
|
||||||
|
'**/*.md'
|
||||||
|
!extensions/**/*.md
|
||||||
|
|||||||
161
.github/workflows/release-trigger.yml
vendored
Normal file
161
.github/workflows/release-trigger.yml
vendored
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
name: Release Trigger
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to release (e.g., 0.1.11). Leave empty to auto-increment patch version.'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bump-version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.RELEASE_PAT }}
|
||||||
|
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Determine version
|
||||||
|
id: version
|
||||||
|
env:
|
||||||
|
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||||
|
run: |
|
||||||
|
if [[ -n "$INPUT_VERSION" ]]; then
|
||||||
|
# Manual version specified - strip optional v prefix
|
||||||
|
VERSION="${INPUT_VERSION#v}"
|
||||||
|
# Validate strict semver format to prevent injection
|
||||||
|
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Error: Invalid version format '$VERSION'. Must be X.Y.Z (e.g. 1.2.3 or v1.2.3)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Using manual version: $VERSION"
|
||||||
|
else
|
||||||
|
# Auto-increment patch version
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
||||||
|
echo "Latest tag: $LATEST_TAG"
|
||||||
|
|
||||||
|
# Extract version number and increment
|
||||||
|
VERSION=$(echo $LATEST_TAG | sed 's/v//')
|
||||||
|
IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
|
||||||
|
MAJOR=${VERSION_PARTS[0]:-0}
|
||||||
|
MINOR=${VERSION_PARTS[1]:-0}
|
||||||
|
PATCH=${VERSION_PARTS[2]:-0}
|
||||||
|
|
||||||
|
# Increment patch version
|
||||||
|
PATCH=$((PATCH + 1))
|
||||||
|
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
||||||
|
|
||||||
|
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Auto-incremented version: $NEW_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check if tag already exists
|
||||||
|
run: |
|
||||||
|
if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
|
||||||
|
echo "Error: Tag ${{ steps.version.outputs.tag }} already exists!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create release branch
|
||||||
|
run: |
|
||||||
|
BRANCH="chore/release-${{ steps.version.outputs.tag }}"
|
||||||
|
git checkout -b "$BRANCH"
|
||||||
|
echo "branch=$BRANCH" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Update pyproject.toml
|
||||||
|
run: |
|
||||||
|
sed -i "s/version = \".*\"/version = \"${{ steps.version.outputs.version }}\"/" pyproject.toml
|
||||||
|
echo "Updated pyproject.toml to version ${{ steps.version.outputs.version }}"
|
||||||
|
|
||||||
|
- name: Update CHANGELOG.md
|
||||||
|
run: |
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
|
# Get the previous tag to compare commits
|
||||||
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
echo "Generating changelog from commits..."
|
||||||
|
if [[ -n "$PREVIOUS_TAG" ]]; then
|
||||||
|
echo "Changes since $PREVIOUS_TAG"
|
||||||
|
COMMITS=$(git log --oneline "$PREVIOUS_TAG"..HEAD --no-merges --pretty=format:"- %s" 2>/dev/null || echo "- Initial release")
|
||||||
|
else
|
||||||
|
echo "No previous tag found - this is the first release"
|
||||||
|
COMMITS="- Initial release"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new changelog entry
|
||||||
|
{
|
||||||
|
head -n 8 CHANGELOG.md
|
||||||
|
echo ""
|
||||||
|
echo "## [${{ steps.version.outputs.version }}] - $DATE"
|
||||||
|
echo ""
|
||||||
|
echo "### Changed"
|
||||||
|
echo ""
|
||||||
|
echo "$COMMITS"
|
||||||
|
echo ""
|
||||||
|
tail -n +9 CHANGELOG.md
|
||||||
|
} > CHANGELOG.md.tmp
|
||||||
|
mv CHANGELOG.md.tmp CHANGELOG.md
|
||||||
|
|
||||||
|
echo "✅ Updated CHANGELOG.md with commits since $PREVIOUS_TAG"
|
||||||
|
else
|
||||||
|
echo "No CHANGELOG.md found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Commit version bump
|
||||||
|
run: |
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
git add pyproject.toml CHANGELOG.md
|
||||||
|
else
|
||||||
|
git add pyproject.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
else
|
||||||
|
git commit -m "chore: bump version to ${{ steps.version.outputs.version }}"
|
||||||
|
echo "Changes committed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create and push tag
|
||||||
|
run: |
|
||||||
|
git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}"
|
||||||
|
git push origin "${{ env.branch }}"
|
||||||
|
git push origin "${{ steps.version.outputs.tag }}"
|
||||||
|
echo "Branch ${{ env.branch }} and tag ${{ steps.version.outputs.tag }} pushed"
|
||||||
|
|
||||||
|
- name: Open pull request
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
|
||||||
|
run: |
|
||||||
|
gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head "${{ env.branch }}" \
|
||||||
|
--title "chore: bump version to ${{ steps.version.outputs.version }}" \
|
||||||
|
--body "Automated version bump to ${{ steps.version.outputs.version }}.
|
||||||
|
|
||||||
|
This PR was created by the Release Trigger workflow. The git tag \`${{ steps.version.outputs.tag }}\` has already been pushed and the release artifacts are being built.
|
||||||
|
|
||||||
|
Merge this PR to record the version bump and changelog update on \`main\`."
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
run: |
|
||||||
|
echo "✅ Version bumped to ${{ steps.version.outputs.version }}"
|
||||||
|
echo "✅ Tag ${{ steps.version.outputs.tag }} created and pushed"
|
||||||
|
echo "✅ PR opened to merge version bump into main"
|
||||||
|
echo "🚀 Release workflow is building artifacts from the tag"
|
||||||
45
.github/workflows/release.yml
vendored
45
.github/workflows/release.yml
vendored
@@ -2,59 +2,60 @@ name: Create Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
tags:
|
||||||
paths:
|
- 'v*'
|
||||||
- 'memory/**'
|
|
||||||
- 'scripts/**'
|
|
||||||
- 'templates/**'
|
|
||||||
- '.github/workflows/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Get latest tag
|
|
||||||
id: get_tag
|
- name: Extract version from tag
|
||||||
|
id: version
|
||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/get-next-version.sh
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
.github/workflows/scripts/get-next-version.sh
|
echo "tag=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Building release for $VERSION"
|
||||||
|
|
||||||
- name: Check if release already exists
|
- name: Check if release already exists
|
||||||
id: check_release
|
id: check_release
|
||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/check-release-exists.sh
|
chmod +x .github/workflows/scripts/check-release-exists.sh
|
||||||
.github/workflows/scripts/check-release-exists.sh ${{ steps.get_tag.outputs.new_version }}
|
.github/workflows/scripts/check-release-exists.sh ${{ steps.version.outputs.tag }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Create release package variants
|
- name: Create release package variants
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
if: steps.check_release.outputs.exists == 'false'
|
||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/create-release-packages.sh
|
chmod +x .github/workflows/scripts/create-release-packages.sh
|
||||||
.github/workflows/scripts/create-release-packages.sh ${{ steps.get_tag.outputs.new_version }}
|
.github/workflows/scripts/create-release-packages.sh ${{ steps.version.outputs.tag }}
|
||||||
|
|
||||||
- name: Generate release notes
|
- name: Generate release notes
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
if: steps.check_release.outputs.exists == 'false'
|
||||||
id: release_notes
|
id: release_notes
|
||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/generate-release-notes.sh
|
chmod +x .github/workflows/scripts/generate-release-notes.sh
|
||||||
.github/workflows/scripts/generate-release-notes.sh ${{ steps.get_tag.outputs.new_version }} ${{ steps.get_tag.outputs.latest_tag }}
|
# Get the previous tag for changelog generation
|
||||||
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 ${{ steps.version.outputs.tag }}^ 2>/dev/null || echo "")
|
||||||
|
# Default to v0.0.0 if no previous tag is found (e.g., first release)
|
||||||
|
if [ -z "$PREVIOUS_TAG" ]; then
|
||||||
|
PREVIOUS_TAG="v0.0.0"
|
||||||
|
fi
|
||||||
|
.github/workflows/scripts/generate-release-notes.sh ${{ steps.version.outputs.tag }} "$PREVIOUS_TAG"
|
||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
if: steps.check_release.outputs.exists == 'false'
|
||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/create-github-release.sh
|
chmod +x .github/workflows/scripts/create-github-release.sh
|
||||||
.github/workflows/scripts/create-github-release.sh ${{ steps.get_tag.outputs.new_version }}
|
.github/workflows/scripts/create-github-release.sh ${{ steps.version.outputs.tag }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Update version in pyproject.toml (for release artifacts only)
|
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
|
||||||
run: |
|
|
||||||
chmod +x .github/workflows/scripts/update-version.sh
|
|
||||||
.github/workflows/scripts/update-version.sh ${{ steps.get_tag.outputs.new_version }}
|
|
||||||
|
|
||||||
|
|||||||
20
.github/workflows/scripts/create-github-release.sh
vendored
Normal file → Executable file
20
.github/workflows/scripts/create-github-release.sh
vendored
Normal file → Executable file
@@ -40,9 +40,25 @@ gh release create "$VERSION" \
|
|||||||
.genreleases/spec-kit-template-roo-ps-"$VERSION".zip \
|
.genreleases/spec-kit-template-roo-ps-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \
|
.genreleases/spec-kit-template-codebuddy-sh-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \
|
.genreleases/spec-kit-template-codebuddy-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-qodercli-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-qodercli-ps-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-amp-sh-"$VERSION".zip \
|
.genreleases/spec-kit-template-amp-sh-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
|
.genreleases/spec-kit-template-amp-ps-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \
|
.genreleases/spec-kit-template-shai-sh-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \
|
.genreleases/spec-kit-template-shai-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-tabnine-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-tabnine-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-kiro-cli-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-kiro-cli-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-agy-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-agy-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-bob-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-bob-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-vibe-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-vibe-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-kimi-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-kimi-ps-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-generic-sh-"$VERSION".zip \
|
||||||
|
.genreleases/spec-kit-template-generic-ps-"$VERSION".zip \
|
||||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||||
--notes-file release_notes.md
|
--notes-file release_notes.md
|
||||||
|
|||||||
537
.github/workflows/scripts/create-release-packages.ps1
vendored
Normal file
537
.github/workflows/scripts/create-release-packages.ps1
vendored
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
#!/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, kiro-cli, bob, qodercli, shai, tabnine, agy, vibe, kimi, generic
|
||||||
|
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Kimi Code skills in .kimi/skills/<name>/SKILL.md format.
|
||||||
|
# Kimi CLI discovers skills as directories containing a SKILL.md file,
|
||||||
|
# invoked with /skill:<name> (e.g. /skill:speckit.specify).
|
||||||
|
function New-KimiSkills {
|
||||||
|
param(
|
||||||
|
[string]$SkillsDir,
|
||||||
|
[string]$ScriptVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
$templates = Get-ChildItem -Path "templates/commands/*.md" -File -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
foreach ($template in $templates) {
|
||||||
|
$name = [System.IO.Path]::GetFileNameWithoutExtension($template.Name)
|
||||||
|
$skillName = "speckit.$name"
|
||||||
|
$skillDir = Join-Path $SkillsDir $skillName
|
||||||
|
New-Item -ItemType Directory -Force -Path $skillDir | Out-Null
|
||||||
|
|
||||||
|
$fileContent = (Get-Content -Path $template.FullName -Raw) -replace "`r`n", "`n"
|
||||||
|
|
||||||
|
# Extract description
|
||||||
|
$description = "Spec Kit: $name workflow"
|
||||||
|
if ($fileContent -match '(?m)^description:\s*(.+)$') {
|
||||||
|
$description = $matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract script command
|
||||||
|
$scriptCommand = "(Missing script command for $ScriptVariant)"
|
||||||
|
if ($fileContent -match "(?m)^\s*${ScriptVariant}:\s*(.+)$") {
|
||||||
|
$scriptCommand = $matches[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract agent_script command from frontmatter if present
|
||||||
|
$agentScriptCommand = ""
|
||||||
|
if ($fileContent -match "(?ms)agent_scripts:.*?^\s*${ScriptVariant}:\s*(.+?)$") {
|
||||||
|
$agentScriptCommand = $matches[1].Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Replace {SCRIPT}, strip scripts sections, rewrite paths
|
||||||
|
$body = $fileContent -replace '\{SCRIPT\}', $scriptCommand
|
||||||
|
if (-not [string]::IsNullOrEmpty($agentScriptCommand)) {
|
||||||
|
$body = $body -replace '\{AGENT_SCRIPT\}', $agentScriptCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = $body -split "`n"
|
||||||
|
$outputLines = @()
|
||||||
|
$inFrontmatter = $false
|
||||||
|
$skipScripts = $false
|
||||||
|
$dashCount = 0
|
||||||
|
|
||||||
|
foreach ($line in $lines) {
|
||||||
|
if ($line -match '^---$') {
|
||||||
|
$outputLines += $line
|
||||||
|
$dashCount++
|
||||||
|
$inFrontmatter = ($dashCount -eq 1)
|
||||||
|
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"
|
||||||
|
$body = $body -replace '\{ARGS\}', '$ARGUMENTS'
|
||||||
|
$body = $body -replace '__AGENT__', 'kimi'
|
||||||
|
$body = Rewrite-Paths -Content $body
|
||||||
|
|
||||||
|
# Strip existing frontmatter, keep only body
|
||||||
|
$templateBody = ""
|
||||||
|
$fmCount = 0
|
||||||
|
$inBody = $false
|
||||||
|
foreach ($line in ($body -split "`n")) {
|
||||||
|
if ($line -match '^---$') {
|
||||||
|
$fmCount++
|
||||||
|
if ($fmCount -eq 2) { $inBody = $true }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ($inBody) { $templateBody += "$line`n" }
|
||||||
|
}
|
||||||
|
|
||||||
|
$skillContent = "---`nname: `"$skillName`"`ndescription: `"$description`"`n---`n`n$templateBody"
|
||||||
|
Set-Content -Path (Join-Path $skillDir "SKILL.md") -Value $skillContent -NoNewline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
$promptsDir = Join-Path $baseDir ".github/prompts"
|
||||||
|
Generate-CopilotPrompts -AgentsDir $agentsDir -PromptsDir $promptsDir
|
||||||
|
|
||||||
|
$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 'md' -ArgFormat '$ARGUMENTS' -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
|
||||||
|
}
|
||||||
|
'kiro-cli' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".kiro/prompts"
|
||||||
|
Generate-Commands -Agent 'kiro-cli' -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
|
||||||
|
}
|
||||||
|
'qodercli' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".qoder/commands"
|
||||||
|
Generate-Commands -Agent 'qodercli' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
'shai' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".shai/commands"
|
||||||
|
Generate-Commands -Agent 'shai' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
'tabnine' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".tabnine/agent/commands"
|
||||||
|
Generate-Commands -Agent 'tabnine' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
$tabnineTemplate = Join-Path 'agent_templates' 'tabnine/TABNINE.md'
|
||||||
|
if (Test-Path $tabnineTemplate) { Copy-Item $tabnineTemplate (Join-Path $baseDir 'TABNINE.md') }
|
||||||
|
}
|
||||||
|
'agy' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".agent/commands"
|
||||||
|
Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
'vibe' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".vibe/prompts"
|
||||||
|
Generate-Commands -Agent 'vibe' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
'kimi' {
|
||||||
|
$skillsDir = Join-Path $baseDir ".kimi/skills"
|
||||||
|
New-Item -ItemType Directory -Force -Path $skillsDir | Out-Null
|
||||||
|
New-KimiSkills -SkillsDir $skillsDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
'generic' {
|
||||||
|
$cmdDir = Join-Path $baseDir ".speckit/commands"
|
||||||
|
Generate-Commands -Agent 'generic' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
throw "Unsupported agent '$Agent'."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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', 'kiro-cli', 'bob', 'qodercli', 'shai', 'tabnine', 'agy', 'vibe', 'kimi', 'generic')
|
||||||
|
$AllScripts = @('sh', 'ps')
|
||||||
|
|
||||||
|
function Normalize-List {
|
||||||
|
param([string]$Input)
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($Input)) {
|
||||||
|
return @()
|
||||||
|
}
|
||||||
|
|
||||||
|
$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)"
|
||||||
|
}
|
||||||
154
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file → Executable file
154
.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>
|
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
||||||
# Version argument should include leading 'v'.
|
# Version argument should include leading 'v'.
|
||||||
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
|
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
|
||||||
# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp (default: all)
|
# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli kimi generic (default: all)
|
||||||
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
|
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
|
||||||
# Examples:
|
# Examples:
|
||||||
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
|
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
|
||||||
@@ -34,7 +34,8 @@ rewrite_paths() {
|
|||||||
sed -E \
|
sed -E \
|
||||||
-e 's@(/?)memory/@.specify/memory/@g' \
|
-e 's@(/?)memory/@.specify/memory/@g' \
|
||||||
-e 's@(/?)scripts/@.specify/scripts/@g' \
|
-e 's@(/?)scripts/@.specify/scripts/@g' \
|
||||||
-e 's@(/?)templates/@.specify/templates/@g'
|
-e 's@(/?)templates/@.specify/templates/@g' \
|
||||||
|
-e 's@\.specify\.specify/@.specify/@g'
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_commands() {
|
generate_commands() {
|
||||||
@@ -95,12 +96,101 @@ generate_commands() {
|
|||||||
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
|
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/speckit.$name.$ext" ;;
|
||||||
md)
|
md)
|
||||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||||
prompt.md)
|
agent.md)
|
||||||
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
echo "$body" > "$output_dir/speckit.$name.$ext" ;;
|
||||||
esac
|
esac
|
||||||
done
|
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"
|
||||||
|
|
||||||
|
cat > "$prompt_file" <<EOF
|
||||||
|
---
|
||||||
|
agent: ${basename}
|
||||||
|
---
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Kimi Code skills in .kimi/skills/<name>/SKILL.md format.
|
||||||
|
# Kimi CLI discovers skills as directories containing a SKILL.md file,
|
||||||
|
# invoked with /skill:<name> (e.g. /skill:speckit.specify).
|
||||||
|
create_kimi_skills() {
|
||||||
|
local skills_dir="$1"
|
||||||
|
local script_variant="$2"
|
||||||
|
|
||||||
|
for template in templates/commands/*.md; do
|
||||||
|
[[ -f "$template" ]] || continue
|
||||||
|
local name
|
||||||
|
name=$(basename "$template" .md)
|
||||||
|
local skill_name="speckit.${name}"
|
||||||
|
local skill_dir="${skills_dir}/${skill_name}"
|
||||||
|
mkdir -p "$skill_dir"
|
||||||
|
|
||||||
|
local file_content
|
||||||
|
file_content=$(tr -d '\r' < "$template")
|
||||||
|
|
||||||
|
# Extract description from frontmatter
|
||||||
|
local description
|
||||||
|
description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
|
||||||
|
[[ -z "$description" ]] && description="Spec Kit: ${name} workflow"
|
||||||
|
|
||||||
|
# Extract script command
|
||||||
|
local script_command
|
||||||
|
script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')
|
||||||
|
[[ -z "$script_command" ]] && script_command="(Missing script command for $script_variant)"
|
||||||
|
|
||||||
|
# Extract agent_script command from frontmatter if present
|
||||||
|
local agent_script_command
|
||||||
|
agent_script_command=$(printf '%s\n' "$file_content" | awk '
|
||||||
|
/^agent_scripts:$/ { in_agent_scripts=1; next }
|
||||||
|
in_agent_scripts && /^[[:space:]]*'"$script_variant"':[[:space:]]*/ {
|
||||||
|
sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, "")
|
||||||
|
print
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
in_agent_scripts && /^[a-zA-Z]/ { in_agent_scripts=0 }
|
||||||
|
')
|
||||||
|
|
||||||
|
# Build body: replace placeholders, strip scripts sections, rewrite paths
|
||||||
|
local body
|
||||||
|
body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
|
||||||
|
if [[ -n $agent_script_command ]]; then
|
||||||
|
body=$(printf '%s\n' "$body" | sed "s|{AGENT_SCRIPT}|${agent_script_command}|g")
|
||||||
|
fi
|
||||||
|
body=$(printf '%s\n' "$body" | awk '
|
||||||
|
/^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
|
||||||
|
in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
|
||||||
|
in_frontmatter && /^agent_scripts:$/ { skip_scripts=1; next }
|
||||||
|
in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
|
||||||
|
in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
|
||||||
|
{ print }
|
||||||
|
')
|
||||||
|
body=$(printf '%s\n' "$body" | sed 's/{ARGS}/\$ARGUMENTS/g' | sed 's/__AGENT__/kimi/g' | rewrite_paths)
|
||||||
|
|
||||||
|
# Strip existing frontmatter and prepend Kimi frontmatter
|
||||||
|
local template_body
|
||||||
|
template_body=$(printf '%s\n' "$body" | awk '/^---/{p++; if(p==2){found=1; next}} found')
|
||||||
|
|
||||||
|
{
|
||||||
|
printf -- '---\n'
|
||||||
|
printf 'name: "%s"\n' "$skill_name"
|
||||||
|
printf 'description: "%s"\n' "$description"
|
||||||
|
printf -- '---\n\n'
|
||||||
|
printf '%s\n' "$template_body"
|
||||||
|
} > "$skill_dir/SKILL.md"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
build_variant() {
|
build_variant() {
|
||||||
local agent=$1 script=$2
|
local agent=$1 script=$2
|
||||||
local base_dir="$GENRELEASES_DIR/sdd-${agent}-package-${script}"
|
local base_dir="$GENRELEASES_DIR/sdd-${agent}-package-${script}"
|
||||||
@@ -119,12 +209,10 @@ build_variant() {
|
|||||||
case $script in
|
case $script in
|
||||||
sh)
|
sh)
|
||||||
[[ -d scripts/bash ]] && { cp -r scripts/bash "$SPEC_DIR/scripts/"; echo "Copied scripts/bash -> .specify/scripts"; }
|
[[ -d scripts/bash ]] && { cp -r scripts/bash "$SPEC_DIR/scripts/"; echo "Copied scripts/bash -> .specify/scripts"; }
|
||||||
# Copy any script files that aren't in variant-specific directories
|
|
||||||
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
ps)
|
ps)
|
||||||
[[ -d scripts/powershell ]] && { cp -r scripts/powershell "$SPEC_DIR/scripts/"; echo "Copied scripts/powershell -> .specify/scripts"; }
|
[[ -d scripts/powershell ]] && { cp -r scripts/powershell "$SPEC_DIR/scripts/"; echo "Copied scripts/powershell -> .specify/scripts"; }
|
||||||
# Copy any script files that aren't in variant-specific directories
|
|
||||||
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -132,11 +220,6 @@ 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"; }
|
[[ -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-agent, opencode): $ARGUMENTS
|
|
||||||
# * TOML (gemini, qwen): {{args}}
|
|
||||||
# This keeps formats readable without extra abstraction.
|
|
||||||
|
|
||||||
case $agent in
|
case $agent in
|
||||||
claude)
|
claude)
|
||||||
mkdir -p "$base_dir/.claude/commands"
|
mkdir -p "$base_dir/.claude/commands"
|
||||||
@@ -146,9 +229,9 @@ build_variant() {
|
|||||||
generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script"
|
generate_commands gemini toml "{{args}}" "$base_dir/.gemini/commands" "$script"
|
||||||
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;;
|
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md "$base_dir/GEMINI.md" ;;
|
||||||
copilot)
|
copilot)
|
||||||
mkdir -p "$base_dir/.github/prompts"
|
mkdir -p "$base_dir/.github/agents"
|
||||||
generate_commands copilot prompt.md "\$ARGUMENTS" "$base_dir/.github/prompts" "$script"
|
generate_commands copilot agent.md "\$ARGUMENTS" "$base_dir/.github/agents" "$script"
|
||||||
# Create VS Code workspace settings
|
generate_copilot_prompts "$base_dir/.github/agents" "$base_dir/.github/prompts"
|
||||||
mkdir -p "$base_dir/.vscode"
|
mkdir -p "$base_dir/.vscode"
|
||||||
[[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json"
|
[[ -f templates/vscode-settings.json ]] && cp templates/vscode-settings.json "$base_dir/.vscode/settings.json"
|
||||||
;;
|
;;
|
||||||
@@ -157,7 +240,7 @@ build_variant() {
|
|||||||
generate_commands cursor-agent md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
|
generate_commands cursor-agent md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
|
||||||
qwen)
|
qwen)
|
||||||
mkdir -p "$base_dir/.qwen/commands"
|
mkdir -p "$base_dir/.qwen/commands"
|
||||||
generate_commands qwen toml "{{args}}" "$base_dir/.qwen/commands" "$script"
|
generate_commands qwen md "\$ARGUMENTS" "$base_dir/.qwen/commands" "$script"
|
||||||
[[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md "$base_dir/QWEN.md" ;;
|
[[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md "$base_dir/QWEN.md" ;;
|
||||||
opencode)
|
opencode)
|
||||||
mkdir -p "$base_dir/.opencode/command"
|
mkdir -p "$base_dir/.opencode/command"
|
||||||
@@ -180,38 +263,62 @@ build_variant() {
|
|||||||
codebuddy)
|
codebuddy)
|
||||||
mkdir -p "$base_dir/.codebuddy/commands"
|
mkdir -p "$base_dir/.codebuddy/commands"
|
||||||
generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;;
|
generate_commands codebuddy md "\$ARGUMENTS" "$base_dir/.codebuddy/commands" "$script" ;;
|
||||||
|
qodercli)
|
||||||
|
mkdir -p "$base_dir/.qoder/commands"
|
||||||
|
generate_commands qodercli md "\$ARGUMENTS" "$base_dir/.qoder/commands" "$script" ;;
|
||||||
amp)
|
amp)
|
||||||
mkdir -p "$base_dir/.agents/commands"
|
mkdir -p "$base_dir/.agents/commands"
|
||||||
generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;;
|
generate_commands amp md "\$ARGUMENTS" "$base_dir/.agents/commands" "$script" ;;
|
||||||
q)
|
shai)
|
||||||
mkdir -p "$base_dir/.amazonq/prompts"
|
mkdir -p "$base_dir/.shai/commands"
|
||||||
generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;;
|
generate_commands shai md "\$ARGUMENTS" "$base_dir/.shai/commands" "$script" ;;
|
||||||
|
tabnine)
|
||||||
|
mkdir -p "$base_dir/.tabnine/agent/commands"
|
||||||
|
generate_commands tabnine toml "{{args}}" "$base_dir/.tabnine/agent/commands" "$script"
|
||||||
|
[[ -f agent_templates/tabnine/TABNINE.md ]] && cp agent_templates/tabnine/TABNINE.md "$base_dir/TABNINE.md" ;;
|
||||||
|
kiro-cli)
|
||||||
|
mkdir -p "$base_dir/.kiro/prompts"
|
||||||
|
generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;;
|
||||||
|
agy)
|
||||||
|
mkdir -p "$base_dir/.agent/commands"
|
||||||
|
generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/commands" "$script" ;;
|
||||||
|
bob)
|
||||||
|
mkdir -p "$base_dir/.bob/commands"
|
||||||
|
generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;;
|
||||||
|
vibe)
|
||||||
|
mkdir -p "$base_dir/.vibe/prompts"
|
||||||
|
generate_commands vibe md "\$ARGUMENTS" "$base_dir/.vibe/prompts" "$script" ;;
|
||||||
|
kimi)
|
||||||
|
mkdir -p "$base_dir/.kimi/skills"
|
||||||
|
create_kimi_skills "$base_dir/.kimi/skills" "$script" ;;
|
||||||
|
generic)
|
||||||
|
mkdir -p "$base_dir/.speckit/commands"
|
||||||
|
generate_commands generic md "\$ARGUMENTS" "$base_dir/.speckit/commands" "$script" ;;
|
||||||
esac
|
esac
|
||||||
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
|
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
|
||||||
echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Determine agent list
|
# Determine agent list
|
||||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp q)
|
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli kimi generic)
|
||||||
ALL_SCRIPTS=(sh ps)
|
ALL_SCRIPTS=(sh ps)
|
||||||
|
|
||||||
norm_list() {
|
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?"\n":"") $i);out=1}}}END{printf("\n")}'
|
||||||
tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?" ":"") $i)}}}END{printf("\n")}'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_subset() {
|
validate_subset() {
|
||||||
local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
|
local type=$1; shift; local -n allowed=$1; shift; local items=("$@")
|
||||||
local ok=1
|
local invalid=0
|
||||||
for it in "${items[@]}"; do
|
for it in "${items[@]}"; do
|
||||||
local found=0
|
local found=0
|
||||||
for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
|
for a in "${allowed[@]}"; do [[ $it == "$a" ]] && { found=1; break; }; done
|
||||||
if [[ $found -eq 0 ]]; then
|
if [[ $found -eq 0 ]]; then
|
||||||
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
|
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
|
||||||
ok=0
|
invalid=1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
return $ok
|
return $invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ -n ${AGENTS:-} ]]; then
|
if [[ -n ${AGENTS:-} ]]; then
|
||||||
@@ -239,4 +346,3 @@ done
|
|||||||
|
|
||||||
echo "Archives in $GENRELEASES_DIR:"
|
echo "Archives in $GENRELEASES_DIR:"
|
||||||
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip
|
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ fi
|
|||||||
cat > release_notes.md << EOF
|
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.
|
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
|
EOF
|
||||||
|
|
||||||
echo "Generated release notes:"
|
echo "Generated release notes:"
|
||||||
|
|||||||
161
.github/workflows/scripts/simulate-release.sh
vendored
Executable file
161
.github/workflows/scripts/simulate-release.sh
vendored
Executable file
@@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# simulate-release.sh
|
||||||
|
# Simulate the release process locally without pushing to GitHub
|
||||||
|
# Usage: simulate-release.sh [version]
|
||||||
|
# If version is omitted, auto-increments patch version
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}🧪 Simulating Release Process Locally${NC}"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 1: Determine version
|
||||||
|
if [[ -n "${1:-}" ]]; then
|
||||||
|
VERSION="${1#v}"
|
||||||
|
TAG="v$VERSION"
|
||||||
|
echo -e "${GREEN}📝 Using manual version: $VERSION${NC}"
|
||||||
|
else
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
||||||
|
echo -e "${BLUE}Latest tag: $LATEST_TAG${NC}"
|
||||||
|
|
||||||
|
VERSION=$(echo $LATEST_TAG | sed 's/v//')
|
||||||
|
IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
|
||||||
|
MAJOR=${VERSION_PARTS[0]:-0}
|
||||||
|
MINOR=${VERSION_PARTS[1]:-0}
|
||||||
|
PATCH=${VERSION_PARTS[2]:-0}
|
||||||
|
|
||||||
|
PATCH=$((PATCH + 1))
|
||||||
|
VERSION="$MAJOR.$MINOR.$PATCH"
|
||||||
|
TAG="v$VERSION"
|
||||||
|
echo -e "${GREEN}📝 Auto-incremented to: $VERSION${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 2: Check if tag exists
|
||||||
|
if git rev-parse "$TAG" >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}❌ Error: Tag $TAG already exists!${NC}"
|
||||||
|
echo " Please use a different version or delete the tag first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ Tag $TAG is available${NC}"
|
||||||
|
|
||||||
|
# Step 3: Backup current state
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}💾 Creating backup of current state...${NC}"
|
||||||
|
BACKUP_DIR=$(mktemp -d)
|
||||||
|
cp pyproject.toml "$BACKUP_DIR/pyproject.toml.bak"
|
||||||
|
cp CHANGELOG.md "$BACKUP_DIR/CHANGELOG.md.bak"
|
||||||
|
echo -e "${GREEN}✓ Backup created at: $BACKUP_DIR${NC}"
|
||||||
|
|
||||||
|
# Step 4: Update pyproject.toml
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}📝 Updating pyproject.toml...${NC}"
|
||||||
|
sed -i.tmp "s/version = \".*\"/version = \"$VERSION\"/" pyproject.toml
|
||||||
|
rm -f pyproject.toml.tmp
|
||||||
|
echo -e "${GREEN}✓ Updated pyproject.toml to version $VERSION${NC}"
|
||||||
|
|
||||||
|
# Step 5: Update CHANGELOG.md
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}📝 Updating CHANGELOG.md...${NC}"
|
||||||
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
|
# Get the previous tag to compare commits
|
||||||
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [[ -n "$PREVIOUS_TAG" ]]; then
|
||||||
|
echo " Generating changelog from commits since $PREVIOUS_TAG"
|
||||||
|
# Get commits since last tag, format as bullet points
|
||||||
|
COMMITS=$(git log --oneline "$PREVIOUS_TAG"..HEAD --no-merges --pretty=format:"- %s" 2>/dev/null || echo "- Initial release")
|
||||||
|
else
|
||||||
|
echo " No previous tag found - this is the first release"
|
||||||
|
COMMITS="- Initial release"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temp file with new entry
|
||||||
|
{
|
||||||
|
head -n 8 CHANGELOG.md
|
||||||
|
echo ""
|
||||||
|
echo "## [$VERSION] - $DATE"
|
||||||
|
echo ""
|
||||||
|
echo "### Changed"
|
||||||
|
echo ""
|
||||||
|
echo "$COMMITS"
|
||||||
|
echo ""
|
||||||
|
tail -n +9 CHANGELOG.md
|
||||||
|
} > CHANGELOG.md.tmp
|
||||||
|
mv CHANGELOG.md.tmp CHANGELOG.md
|
||||||
|
echo -e "${GREEN}✓ Updated CHANGELOG.md with commits since $PREVIOUS_TAG${NC}"
|
||||||
|
|
||||||
|
# Step 6: Show what would be committed
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}📋 Changes that would be committed:${NC}"
|
||||||
|
git diff pyproject.toml CHANGELOG.md
|
||||||
|
|
||||||
|
# Step 7: Create temporary tag (no push)
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}🏷️ Creating temporary local tag...${NC}"
|
||||||
|
git tag -a "$TAG" -m "Simulated release $TAG" 2>/dev/null || true
|
||||||
|
echo -e "${GREEN}✓ Tag $TAG created locally${NC}"
|
||||||
|
|
||||||
|
# Step 8: Simulate release artifact creation
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}📦 Simulating release package creation...${NC}"
|
||||||
|
echo " (High-level simulation only; packaging script is not executed)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if script exists and is executable
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
if [[ -x "$SCRIPT_DIR/create-release-packages.sh" ]]; then
|
||||||
|
echo -e "${BLUE}In a real release, the following command would be run to create packages:${NC}"
|
||||||
|
echo " $SCRIPT_DIR/create-release-packages.sh \"$TAG\""
|
||||||
|
echo ""
|
||||||
|
echo "This simulation does not enumerate individual package files to avoid"
|
||||||
|
echo "drifting from the actual behavior of create-release-packages.sh."
|
||||||
|
else
|
||||||
|
echo -e "${RED}⚠️ create-release-packages.sh not found or not executable${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 9: Simulate release notes generation
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}📄 Simulating release notes generation...${NC}"
|
||||||
|
echo ""
|
||||||
|
PREVIOUS_TAG=$(git describe --tags --abbrev=0 $TAG^ 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$PREVIOUS_TAG" ]]; then
|
||||||
|
echo -e "${BLUE}Changes since $PREVIOUS_TAG:${NC}"
|
||||||
|
git log --oneline "$PREVIOUS_TAG".."$TAG" | head -n 10
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo -e "${BLUE}No previous tag found - this would be the first release${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 10: Summary
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}🎉 Simulation Complete!${NC}"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Summary:${NC}"
|
||||||
|
echo " Version: $VERSION"
|
||||||
|
echo " Tag: $TAG"
|
||||||
|
echo " Backup: $BACKUP_DIR"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}⚠️ SIMULATION ONLY - NO CHANGES PUSHED${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Next steps:${NC}"
|
||||||
|
echo " 1. Review the changes above"
|
||||||
|
echo " 2. To keep changes: git add pyproject.toml CHANGELOG.md && git commit"
|
||||||
|
echo " 3. To discard changes: git checkout pyproject.toml CHANGELOG.md && git tag -d $TAG"
|
||||||
|
echo " 4. To restore from backup: cp $BACKUP_DIR/* ."
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}To run the actual release:${NC}"
|
||||||
|
echo " Go to: https://github.com/github/spec-kit/actions/workflows/release-trigger.yml"
|
||||||
|
echo " Click 'Run workflow' and enter version: $VERSION"
|
||||||
|
echo ""
|
||||||
42
.github/workflows/stale.yml
vendored
Normal file
42
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: 'Close stale issues and PRs'
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *' # Run daily at midnight UTC
|
||||||
|
workflow_dispatch: # Allow manual triggering
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v10
|
||||||
|
with:
|
||||||
|
# Days of inactivity before an issue or PR becomes stale
|
||||||
|
days-before-stale: 150
|
||||||
|
# Days of inactivity before a stale issue or PR is closed (after being marked stale)
|
||||||
|
days-before-close: 30
|
||||||
|
|
||||||
|
# Stale issue settings
|
||||||
|
stale-issue-message: 'This issue has been automatically marked as stale because it has not had any activity for 150 days. It will be closed in 30 days if no further activity occurs.'
|
||||||
|
close-issue-message: 'This issue has been automatically closed due to inactivity (180 days total). If you believe this issue is still relevant, please reopen it or create a new issue.'
|
||||||
|
stale-issue-label: 'stale'
|
||||||
|
|
||||||
|
# Stale PR settings
|
||||||
|
stale-pr-message: 'This pull request has been automatically marked as stale because it has not had any activity for 150 days. It will be closed in 30 days if no further activity occurs.'
|
||||||
|
close-pr-message: 'This pull request has been automatically closed due to inactivity (180 days total). If you believe this PR is still relevant, please reopen it or create a new PR.'
|
||||||
|
stale-pr-label: 'stale'
|
||||||
|
|
||||||
|
# Exempt issues and PRs with these labels from being marked as stale
|
||||||
|
exempt-issue-labels: 'pinned,security'
|
||||||
|
exempt-pr-labels: 'pinned,security'
|
||||||
|
|
||||||
|
# Only issues or PRs with all of these labels are checked
|
||||||
|
# Leave empty to check all issues and PRs
|
||||||
|
any-of-labels: ''
|
||||||
|
|
||||||
|
# Operations per run (helps avoid rate limits)
|
||||||
|
operations-per-run: 100
|
||||||
50
.github/workflows/test.yml
vendored
Normal file
50
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Test & Lint Python
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ruff:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: "3.13"
|
||||||
|
|
||||||
|
- name: Run ruff check
|
||||||
|
run: uvx ruff check src/
|
||||||
|
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.11", "3.12", "3.13"]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --extra test
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: uv run pytest
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -32,6 +32,7 @@ env/
|
|||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.tmp
|
||||||
|
|
||||||
# Project specific
|
# Project specific
|
||||||
*.log
|
*.log
|
||||||
@@ -43,3 +44,9 @@ env/
|
|||||||
.genreleases/
|
.genreleases/
|
||||||
*.zip
|
*.zip
|
||||||
sdd-*/
|
sdd-*/
|
||||||
|
docs/dev
|
||||||
|
|
||||||
|
# Extension system
|
||||||
|
.specify/extensions/.cache/
|
||||||
|
.specify/extensions/.backup/
|
||||||
|
.specify/extensions/*/local-config.yml
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
},
|
},
|
||||||
"MD050": {
|
"MD050": {
|
||||||
"style": "asterisk"
|
"style": "asterisk"
|
||||||
}
|
},
|
||||||
}
|
"MD036": false,
|
||||||
|
"MD060": false
|
||||||
|
},
|
||||||
|
"ignores": [
|
||||||
|
".genreleases/"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
77
AGENTS.md
77
AGENTS.md
@@ -10,10 +10,6 @@ The toolkit supports multiple AI coding assistants, allowing teams to use their
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## General practices
|
|
||||||
|
|
||||||
- Any changes to `__init__.py` for the Specify CLI require a version rev in `pyproject.toml` and addition of entries to `CHANGELOG.md`.
|
|
||||||
|
|
||||||
## Adding New Agent Support
|
## Adding New Agent Support
|
||||||
|
|
||||||
This section explains how to add support for new AI agents/assistants to the Specify CLI. Use this guide as a reference when integrating new AI tools into the Spec-Driven Development workflow.
|
This section explains how to add support for new AI agents/assistants to the Specify CLI. Use this guide as a reference when integrating new AI tools into the Spec-Driven Development workflow.
|
||||||
@@ -29,22 +25,28 @@ Specify supports multiple AI agents by generating agent-specific command files a
|
|||||||
|
|
||||||
### Current Supported Agents
|
### Current Supported Agents
|
||||||
|
|
||||||
| Agent | Directory | Format | CLI Tool | Description |
|
| Agent | Directory | Format | CLI Tool | Description |
|
||||||
|-------|-----------|---------|----------|-------------|
|
| -------------------------- | ---------------------- | -------- | --------------- | --------------------------- |
|
||||||
| **Claude Code** | `.claude/commands/` | Markdown | `claude` | Anthropic's Claude Code CLI |
|
| **Claude Code** | `.claude/commands/` | Markdown | `claude` | Anthropic's Claude Code CLI |
|
||||||
| **Gemini CLI** | `.gemini/commands/` | TOML | `gemini` | Google's Gemini CLI |
|
| **Gemini CLI** | `.gemini/commands/` | TOML | `gemini` | Google's Gemini CLI |
|
||||||
| **GitHub Copilot** | `.github/prompts/` | Markdown | N/A (IDE-based) | GitHub Copilot in VS Code |
|
| **GitHub Copilot** | `.github/agents/` | Markdown | N/A (IDE-based) | GitHub Copilot in VS Code |
|
||||||
| **Cursor** | `.cursor/commands/` | Markdown | `cursor-agent` | Cursor CLI |
|
| **Cursor** | `.cursor/commands/` | Markdown | `cursor-agent` | Cursor CLI |
|
||||||
| **Qwen Code** | `.qwen/commands/` | TOML | `qwen` | Alibaba's Qwen Code CLI |
|
| **Qwen Code** | `.qwen/commands/` | Markdown | `qwen` | Alibaba's Qwen Code CLI |
|
||||||
| **opencode** | `.opencode/command/` | Markdown | `opencode` | opencode CLI |
|
| **opencode** | `.opencode/command/` | Markdown | `opencode` | opencode CLI |
|
||||||
| **Codex CLI** | `.codex/commands/` | Markdown | `codex` | Codex CLI |
|
| **Codex CLI** | `.codex/commands/` | Markdown | `codex` | Codex CLI |
|
||||||
| **Windsurf** | `.windsurf/workflows/` | Markdown | N/A (IDE-based) | Windsurf IDE workflows |
|
| **Windsurf** | `.windsurf/workflows/` | Markdown | N/A (IDE-based) | Windsurf IDE workflows |
|
||||||
| **Kilo Code** | `.kilocode/rules/` | Markdown | N/A (IDE-based) | Kilo Code IDE |
|
| **Kilo Code** | `.kilocode/rules/` | Markdown | N/A (IDE-based) | Kilo Code IDE |
|
||||||
| **Auggie CLI** | `.augment/rules/` | Markdown | `auggie` | Auggie CLI |
|
| **Auggie CLI** | `.augment/rules/` | Markdown | `auggie` | Auggie CLI |
|
||||||
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
|
| **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE |
|
||||||
| **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI |
|
| **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI |
|
||||||
| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI |
|
| **Qoder CLI** | `.qoder/commands/` | Markdown | `qodercli` | Qoder CLI |
|
||||||
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |
|
| **Kiro CLI** | `.kiro/prompts/` | Markdown | `kiro-cli` | Kiro CLI |
|
||||||
|
| **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI |
|
||||||
|
| **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI |
|
||||||
|
| **Tabnine CLI** | `.tabnine/agent/commands/` | TOML | `tabnine` | Tabnine CLI |
|
||||||
|
| **Kimi Code** | `.kimi/skills/` | Markdown | `kimi` | Kimi Code CLI (Moonshot AI) |
|
||||||
|
| **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE |
|
||||||
|
| **Generic** | User-specified via `--ai-commands-dir` | Markdown | N/A | Bring your own agent |
|
||||||
|
|
||||||
### Step-by-Step Integration Guide
|
### Step-by-Step Integration Guide
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ AGENT_CONFIG = {
|
|||||||
"new-agent-cli": { # Use the ACTUAL CLI tool name (what users type in terminal)
|
"new-agent-cli": { # Use the ACTUAL CLI tool name (what users type in terminal)
|
||||||
"name": "New Agent Display Name",
|
"name": "New Agent Display Name",
|
||||||
"folder": ".newagent/", # Directory for agent files
|
"folder": ".newagent/", # Directory for agent files
|
||||||
|
"commands_subdir": "commands", # Subdirectory name for command files (default: "commands")
|
||||||
"install_url": "https://example.com/install", # URL for installation docs (or None if IDE-based)
|
"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
|
"requires_cli": True, # True if CLI tool required, False for IDE-based agents
|
||||||
},
|
},
|
||||||
@@ -79,6 +82,10 @@ This eliminates the need for special-case mappings throughout the codebase.
|
|||||||
|
|
||||||
- `name`: Human-readable display name shown to users
|
- `name`: Human-readable display name shown to users
|
||||||
- `folder`: Directory where agent-specific files are stored (relative to project root)
|
- `folder`: Directory where agent-specific files are stored (relative to project root)
|
||||||
|
- `commands_subdir`: Subdirectory name within the agent folder where command/prompt files are stored (default: `"commands"`)
|
||||||
|
- Most agents use `"commands"` (e.g., `.claude/commands/`)
|
||||||
|
- Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular)
|
||||||
|
- This field enables `--ai-skills` to locate command templates correctly for skill generation
|
||||||
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
|
- `install_url`: Installation documentation URL (set to `None` for IDE-based agents)
|
||||||
- `requires_cli`: Whether the agent requires a CLI tool check during initialization
|
- `requires_cli`: Whether the agent requires a CLI tool check during initialization
|
||||||
|
|
||||||
@@ -87,7 +94,7 @@ This eliminates the need for special-case mappings throughout the codebase.
|
|||||||
Update the `--ai` parameter help text in the `init()` command to include the new agent:
|
Update the `--ai` parameter help text in the `init()` command to include the new agent:
|
||||||
|
|
||||||
```python
|
```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"),
|
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 kiro-cli"),
|
||||||
```
|
```
|
||||||
|
|
||||||
Also update any function docstrings, examples, and error messages that list available agents.
|
Also update any function docstrings, examples, and error messages that list available agents.
|
||||||
@@ -108,7 +115,7 @@ Modify `.github/workflows/scripts/create-release-packages.sh`:
|
|||||||
##### Add to ALL_AGENTS array
|
##### Add to ALL_AGENTS array
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf q)
|
ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf kiro-cli)
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Add case statement for directory structure
|
##### Add case statement for directory structure
|
||||||
@@ -308,9 +315,13 @@ Require a command-line tool to be installed:
|
|||||||
- **Cursor**: `cursor-agent` CLI
|
- **Cursor**: `cursor-agent` CLI
|
||||||
- **Qwen Code**: `qwen` CLI
|
- **Qwen Code**: `qwen` CLI
|
||||||
- **opencode**: `opencode` CLI
|
- **opencode**: `opencode` CLI
|
||||||
- **Amazon Q Developer CLI**: `q` CLI
|
- **Kiro CLI**: `kiro-cli` CLI
|
||||||
- **CodeBuddy CLI**: `codebuddy` CLI
|
- **CodeBuddy CLI**: `codebuddy` CLI
|
||||||
|
- **Qoder CLI**: `qodercli` CLI
|
||||||
- **Amp**: `amp` CLI
|
- **Amp**: `amp` CLI
|
||||||
|
- **SHAI**: `shai` CLI
|
||||||
|
- **Tabnine CLI**: `tabnine` CLI
|
||||||
|
- **Kimi Code**: `kimi` CLI
|
||||||
|
|
||||||
### IDE-Based Agents
|
### IDE-Based Agents
|
||||||
|
|
||||||
@@ -318,12 +329,15 @@ Work within integrated development environments:
|
|||||||
|
|
||||||
- **GitHub Copilot**: Built into VS Code/compatible editors
|
- **GitHub Copilot**: Built into VS Code/compatible editors
|
||||||
- **Windsurf**: Built into Windsurf IDE
|
- **Windsurf**: Built into Windsurf IDE
|
||||||
|
- **IBM Bob**: Built into IBM Bob IDE
|
||||||
|
|
||||||
## Command File Formats
|
## Command File Formats
|
||||||
|
|
||||||
### Markdown Format
|
### Markdown Format
|
||||||
|
|
||||||
Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp
|
Used by: Claude, Cursor, opencode, Windsurf, Kiro CLI, Amp, SHAI, IBM Bob, Kimi Code, Qwen
|
||||||
|
|
||||||
|
**Standard format:**
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
---
|
---
|
||||||
@@ -333,9 +347,20 @@ description: "Command description"
|
|||||||
Command content with {SCRIPT} and $ARGUMENTS placeholders.
|
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
|
### TOML Format
|
||||||
|
|
||||||
Used by: Gemini, Qwen
|
Used by: Gemini, Tabnine
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
description = "Command description"
|
description = "Command description"
|
||||||
@@ -349,7 +374,7 @@ Command content with {SCRIPT} and {{args}} placeholders.
|
|||||||
|
|
||||||
- **CLI agents**: Usually `.<agent-name>/commands/`
|
- **CLI agents**: Usually `.<agent-name>/commands/`
|
||||||
- **IDE agents**: Follow IDE-specific patterns:
|
- **IDE agents**: Follow IDE-specific patterns:
|
||||||
- Copilot: `.github/prompts/`
|
- Copilot: `.github/agents/`
|
||||||
- Cursor: `.cursor/commands/`
|
- Cursor: `.cursor/commands/`
|
||||||
- Windsurf: `.windsurf/workflows/`
|
- Windsurf: `.windsurf/workflows/`
|
||||||
|
|
||||||
|
|||||||
411
CHANGELOG.md
411
CHANGELOG.md
@@ -2,166 +2,335 @@
|
|||||||
|
|
||||||
<!-- markdownlint-disable MD024 -->
|
<!-- markdownlint-disable MD024 -->
|
||||||
|
|
||||||
All notable changes to the Specify CLI and templates are documented here.
|
Recent changes to the Specify CLI and templates are documented here.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [0.0.20] - 2025-10-14
|
## [0.3.0] - 2026-03-13
|
||||||
|
|
||||||
### 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
|
### Changed
|
||||||
|
|
||||||
- Enhanced help documentation for `create-new-feature.sh` and `create-new-feature.ps1` scripts with examples
|
- feat(presets): Pluggable preset system with catalog, resolver, and skills propagation (#1787)
|
||||||
- Branch names now validated against GitHub's 244-byte limit with automatic truncation if needed
|
- fix: match 'Last updated' timestamp with or without bold markers (#1836)
|
||||||
|
- Add specify doctor command for project health diagnostics (#1828)
|
||||||
|
- fix: harden bash scripts against shell injection and improve robustness (#1809)
|
||||||
|
- fix: clean up command templates (specify, analyze) (#1810)
|
||||||
|
- fix: migrate Qwen Code CLI from TOML to Markdown format (#1589) (#1730)
|
||||||
|
- fix(cli): deprecate explicit command support for agy (#1798) (#1808)
|
||||||
|
- Add /selftest.extension core extension to test other extensions (#1758)
|
||||||
|
- feat(extensions): Quality of life improvements for RFC-aligned catalog integration (#1776)
|
||||||
|
- Add Java brownfield walkthrough to community walkthroughs (#1820)
|
||||||
|
- chore: bump version to 0.2.1 (#1813)
|
||||||
|
- Added February 2026 newsletter (#1812)
|
||||||
|
- feat: add Kimi Code CLI agent support (#1790)
|
||||||
|
- docs: fix broken links in quickstart guide (#1759) (#1797)
|
||||||
|
- docs: add catalog cli help documentation (#1793) (#1794)
|
||||||
|
- fix: use quiet checkout to avoid exception on git checkout (#1792)
|
||||||
|
- feat(extensions): support .extensionignore to exclude files during install (#1781)
|
||||||
|
- feat: add Codex support for extension command registration (#1767)
|
||||||
|
- chore: bump version to 0.2.0 (#1786)
|
||||||
|
- fix: sync agent list comments with actual supported agents (#1785)
|
||||||
|
- feat(extensions): support multiple active catalogs simultaneously (#1720)
|
||||||
|
- Pavel/add tabnine cli support (#1503)
|
||||||
|
- Add Understanding extension to community catalog (#1778)
|
||||||
|
- Add ralph extension to community catalog (#1780)
|
||||||
|
- Update README with project initialization instructions (#1772)
|
||||||
|
- feat: add review extension to community catalog (#1775)
|
||||||
|
- Add fleet extension to community catalog (#1771)
|
||||||
|
- Integration of Mistral vibe support into speckit (#1725)
|
||||||
|
- fix: Remove duplicate options in specify.md (#1765)
|
||||||
|
- fix: use global branch numbering instead of per-short-name detection (#1757)
|
||||||
|
- Add Community Walkthroughs section to README (#1766)
|
||||||
|
- feat(extensions): add Jira Integration to community catalog (#1764)
|
||||||
|
- Add Azure DevOps Integration extension to community catalog (#1734)
|
||||||
|
- Fix docs: update Antigravity link and add initialization example (#1748)
|
||||||
|
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
|
||||||
|
- make c ignores consistent with c++ (#1747)
|
||||||
|
- chore: bump version to 0.1.13 (#1746)
|
||||||
|
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
|
||||||
|
- feat: add verify extension to community catalog (#1726)
|
||||||
|
- Add Retrospective Extension to community catalog README table (#1741)
|
||||||
|
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
|
||||||
|
- fix: correct Copilot extension command registration (#1724)
|
||||||
|
- fix(implement): remove Makefile from C ignore patterns (#1558)
|
||||||
|
- Add sync extension to community catalog (#1728)
|
||||||
|
- fix(checklist): clarify file handling behavior for append vs create (#1556)
|
||||||
|
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
|
||||||
|
- chore: bump version to 0.1.12 (#1737)
|
||||||
|
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
|
||||||
|
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
|
||||||
|
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
|
||||||
|
|
||||||
## [0.0.19] - 2025-10-10
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for CodeBuddy (thank you to [@lispking](https://github.com/lispking) for the contribution).
|
- feat(presets): Pluggable preset system with preset catalog and template resolver
|
||||||
- You can now see Git-sourced errors in the Specify CLI.
|
- Preset manifest (`preset.yml`) with validation for artifact, command, and script types
|
||||||
|
- `PresetManifest`, `PresetRegistry`, `PresetManager`, `PresetCatalog`, `PresetResolver` classes in `src/specify_cli/presets.py`
|
||||||
|
- CLI commands: `specify preset search`, `specify preset add`, `specify preset list`, `specify preset remove`, `specify preset resolve`, `specify preset info`
|
||||||
|
- CLI commands: `specify preset catalog list`, `specify preset catalog add`, `specify preset catalog remove` for multi-catalog management
|
||||||
|
- `PresetCatalogEntry` dataclass and multi-catalog support mirroring the extension catalog system
|
||||||
|
- `--preset` option for `specify init` to install presets during initialization
|
||||||
|
- Priority-based preset resolution: presets with lower priority number win (`--priority` flag)
|
||||||
|
- `resolve_template()` / `Resolve-Template` helpers in bash and PowerShell common scripts
|
||||||
|
- Template resolution priority stack: overrides → presets → extensions → core
|
||||||
|
- Preset catalog files (`presets/catalog.json`, `presets/catalog.community.json`)
|
||||||
|
- Preset scaffold directory (`presets/scaffold/`)
|
||||||
|
- Scripts updated to use template resolution instead of hardcoded paths
|
||||||
|
- feat(presets): Preset command overrides now propagate to agent skills when `--ai-skills` was used during init
|
||||||
|
- feat: `specify init` persists CLI options to `.specify/init-options.json` for downstream operations
|
||||||
|
- feat(extensions): support `.extensionignore` to exclude files/folders during `specify extension add` (#1781)
|
||||||
|
|
||||||
|
## [0.2.1] - 2026-03-11
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Fixed the path to the constitution in `plan.md` (thank you to [@lyzno1](https://github.com/lyzno1) for spotting).
|
- Added February 2026 newsletter (#1812)
|
||||||
- Fixed backslash escapes in generated TOML files for Gemini (thank you to [@hsin19](https://github.com/hsin19) for the contribution).
|
- feat: add Kimi Code CLI agent support (#1790)
|
||||||
- Implementation command now ensures that the correct ignore files are added (thank you to [@sigent-amazon](https://github.com/sigent-amazon) for the contribution).
|
- docs: fix broken links in quickstart guide (#1759) (#1797)
|
||||||
|
- docs: add catalog cli help documentation (#1793) (#1794)
|
||||||
|
- fix: use quiet checkout to avoid exception on git checkout (#1792)
|
||||||
|
- feat(extensions): support .extensionignore to exclude files during install (#1781)
|
||||||
|
- feat: add Codex support for extension command registration (#1767)
|
||||||
|
- chore: bump version to 0.2.0 (#1786)
|
||||||
|
- fix: sync agent list comments with actual supported agents (#1785)
|
||||||
|
- feat(extensions): support multiple active catalogs simultaneously (#1720)
|
||||||
|
- Pavel/add tabnine cli support (#1503)
|
||||||
|
- Add Understanding extension to community catalog (#1778)
|
||||||
|
- Add ralph extension to community catalog (#1780)
|
||||||
|
- Update README with project initialization instructions (#1772)
|
||||||
|
- feat: add review extension to community catalog (#1775)
|
||||||
|
- Add fleet extension to community catalog (#1771)
|
||||||
|
- Integration of Mistral vibe support into speckit (#1725)
|
||||||
|
- fix: Remove duplicate options in specify.md (#1765)
|
||||||
|
- fix: use global branch numbering instead of per-short-name detection (#1757)
|
||||||
|
- Add Community Walkthroughs section to README (#1766)
|
||||||
|
- feat(extensions): add Jira Integration to community catalog (#1764)
|
||||||
|
- Add Azure DevOps Integration extension to community catalog (#1734)
|
||||||
|
- Fix docs: update Antigravity link and add initialization example (#1748)
|
||||||
|
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
|
||||||
|
- make c ignores consistent with c++ (#1747)
|
||||||
|
- chore: bump version to 0.1.13 (#1746)
|
||||||
|
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
|
||||||
|
- feat: add verify extension to community catalog (#1726)
|
||||||
|
- Add Retrospective Extension to community catalog README table (#1741)
|
||||||
|
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
|
||||||
|
- fix: correct Copilot extension command registration (#1724)
|
||||||
|
- fix(implement): remove Makefile from C ignore patterns (#1558)
|
||||||
|
- Add sync extension to community catalog (#1728)
|
||||||
|
- fix(checklist): clarify file handling behavior for append vs create (#1556)
|
||||||
|
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
|
||||||
|
- chore: bump version to 0.1.12 (#1737)
|
||||||
|
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
|
||||||
|
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
|
||||||
|
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
|
||||||
|
|
||||||
## [0.0.18] - 2025-10-06
|
## [0.2.0] - 2026-03-09
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Support for using `.` as a shorthand for current directory in `specify init .` command, equivalent to `--here` flag but more intuitive for users.
|
|
||||||
- Use the `/speckit.` command prefix to easily discover Spec Kit-related commands.
|
|
||||||
- Refactor the prompts and templates to simplify their capabilities and how they are tracked. No more polluting things with tests when they are not needed.
|
|
||||||
- Ensure that tasks are created per user story (simplifies testing and validation).
|
|
||||||
- Add support for Visual Studio Code prompt shortcuts and automatic script execution.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- All command files now prefixed with `speckit.` (e.g., `speckit.specify.md`, `speckit.plan.md`) for better discoverability and differentiation in IDE/CLI command palettes and file explorers
|
- feat: add Kimi Code CLI agent support
|
||||||
|
- fix: sync agent list comments with actual supported agents (#1785)
|
||||||
|
- feat(extensions): support multiple active catalogs simultaneously (#1720)
|
||||||
|
- Pavel/add tabnine cli support (#1503)
|
||||||
|
- Add Understanding extension to community catalog (#1778)
|
||||||
|
- Add ralph extension to community catalog (#1780)
|
||||||
|
- Update README with project initialization instructions (#1772)
|
||||||
|
- feat: add review extension to community catalog (#1775)
|
||||||
|
- Add fleet extension to community catalog (#1771)
|
||||||
|
- Integration of Mistral vibe support into speckit (#1725)
|
||||||
|
- fix: Remove duplicate options in specify.md (#1765)
|
||||||
|
- fix: use global branch numbering instead of per-short-name detection (#1757)
|
||||||
|
- Add Community Walkthroughs section to README (#1766)
|
||||||
|
- feat(extensions): add Jira Integration to community catalog (#1764)
|
||||||
|
- Add Azure DevOps Integration extension to community catalog (#1734)
|
||||||
|
- Fix docs: update Antigravity link and add initialization example (#1748)
|
||||||
|
- fix: wire after_tasks and after_implement hook events into command templates (#1702)
|
||||||
|
- make c ignores consistent with c++ (#1747)
|
||||||
|
- chore: bump version to 0.1.13 (#1746)
|
||||||
|
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
|
||||||
|
- feat: add verify extension to community catalog (#1726)
|
||||||
|
- Add Retrospective Extension to community catalog README table (#1741)
|
||||||
|
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
|
||||||
|
- fix: correct Copilot extension command registration (#1724)
|
||||||
|
- fix(implement): remove Makefile from C ignore patterns (#1558)
|
||||||
|
- Add sync extension to community catalog (#1728)
|
||||||
|
- fix(checklist): clarify file handling behavior for append vs create (#1556)
|
||||||
|
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
|
||||||
|
- chore: bump version to 0.1.12 (#1737)
|
||||||
|
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
|
||||||
|
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
|
||||||
|
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
|
||||||
|
|
||||||
## [0.0.17] - 2025-09-22
|
## [0.1.14] - 2026-03-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- 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.
|
- feat: add Tabnine CLI agent support
|
||||||
- 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`.
|
- **Multi-Catalog Support (#1707)**: Extension catalog system now supports multiple active catalogs simultaneously via a catalog stack
|
||||||
- Note: Constitution rules are explicitly treated as non-negotiable; any conflict is a CRITICAL finding requiring artifact remediation, not weakening of principles.
|
- New `specify extension catalog list` command lists all active catalogs with name, URL, priority, and `install_allowed` status
|
||||||
|
- New `specify extension catalog add` and `specify extension catalog remove` commands for project-scoped catalog management
|
||||||
|
- Default built-in stack includes `catalog.json` (default, installable) and `catalog.community.json` (community, discovery only) — community extensions are now surfaced in search results out of the box
|
||||||
|
- `specify extension search` aggregates results across all active catalogs, annotating each result with source catalog
|
||||||
|
- `specify extension add` enforces `install_allowed` policy — extensions from discovery-only catalogs cannot be installed directly
|
||||||
|
- Project-level `.specify/extension-catalogs.yml` and user-level `~/.specify/extension-catalogs.yml` config files supported, with project-level taking precedence
|
||||||
|
- `SPECKIT_CATALOG_URL` environment variable still works for backward compatibility (replaces full stack with single catalog)
|
||||||
|
- All catalog URLs require HTTPS (HTTP allowed for localhost development)
|
||||||
|
- New `CatalogEntry` dataclass in `extensions.py` for catalog stack representation
|
||||||
|
- Per-URL hash-based caching for non-default catalogs; legacy cache preserved for default catalog
|
||||||
|
- Higher-priority catalogs win on merge conflicts (same extension id in multiple catalogs)
|
||||||
|
- 13 new tests covering catalog stack resolution, merge conflicts, URL validation, and `install_allowed` enforcement
|
||||||
|
- Updated RFC, Extension User Guide, and Extension API Reference documentation
|
||||||
|
|
||||||
## [0.0.16] - 2025-09-22
|
## [0.1.13] - 2026-03-03
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- `--force` flag for `init` command to bypass confirmation when using `--here` in a non-empty directory and proceed with merging/overwriting files.
|
|
||||||
|
|
||||||
## [0.0.15] - 2025-09-21
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Support for Roo Code.
|
|
||||||
|
|
||||||
## [0.0.14] - 2025-09-21
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Error messages are now shown consistently.
|
- feat: add kiro-cli and AGENT_CONFIG consistency coverage (#1690)
|
||||||
|
- feat: add verify extension to community catalog (#1726)
|
||||||
|
- Add Retrospective Extension to community catalog README table (#1741)
|
||||||
|
- fix(scripts): add empty description validation and branch checkout error handling (#1559)
|
||||||
|
- fix: correct Copilot extension command registration (#1724)
|
||||||
|
- fix(implement): remove Makefile from C ignore patterns (#1558)
|
||||||
|
- Add sync extension to community catalog (#1728)
|
||||||
|
- fix(checklist): clarify file handling behavior for append vs create (#1556)
|
||||||
|
- fix(clarify): correct conflicting question limit from 10 to 5 (#1557)
|
||||||
|
- chore: bump version to 0.1.12 (#1737)
|
||||||
|
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
|
||||||
|
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
|
||||||
|
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
|
||||||
|
|
||||||
## [0.0.13] - 2025-09-21
|
|
||||||
|
|
||||||
### Added
|
## [0.1.13] - 2026-03-03
|
||||||
|
|
||||||
- Support for Kilo Code. Thank you [@shahrukhkhan489](https://github.com/shahrukhkhan489) with [#394](https://github.com/github/spec-kit/pull/394).
|
|
||||||
- Support for Auggie CLI. Thank you [@hungthai1401](https://github.com/hungthai1401) with [#137](https://github.com/github/spec-kit/pull/137).
|
|
||||||
- Agent folder security notice displayed after project provisioning completion, warning users that some agents may store credentials or auth tokens in their agent folders and recommending adding relevant folders to `.gitignore` to prevent accidental credential leakage.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Warning displayed to ensure that folks are aware that they might need to add their agent folder to `.gitignore`.
|
|
||||||
- Cleaned up the `check` command output.
|
|
||||||
|
|
||||||
## [0.0.12] - 2025-09-21
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Added additional context for OpenAI Codex users - they need to set an additional environment variable, as described in [#417](https://github.com/github/spec-kit/issues/417).
|
|
||||||
|
|
||||||
## [0.0.11] - 2025-09-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Codex CLI support (thank you [@honjo-hiroaki-gtt](https://github.com/honjo-hiroaki-gtt) for the contribution in [#14](https://github.com/github/spec-kit/pull/14))
|
|
||||||
- Codex-aware context update tooling (Bash and PowerShell) so feature plans refresh `AGENTS.md` alongside existing assistants without manual edits.
|
|
||||||
|
|
||||||
## [0.0.10] - 2025-09-20
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Addressed [#378](https://github.com/github/spec-kit/issues/378) where a GitHub token may be attached to the request when it was empty.
|
- **Copilot Extension Commands Not Visible**: Fixed extension commands not appearing in GitHub Copilot when installed via `specify extension add --dev`
|
||||||
|
- Changed Copilot file extension from `.md` to `.agent.md` in `CommandRegistrar.AGENT_CONFIGS` so Copilot recognizes agent files
|
||||||
## [0.0.9] - 2025-09-19
|
- Added generation of companion `.prompt.md` files in `.github/prompts/` during extension command registration, matching the release packaging behavior
|
||||||
|
- Added cleanup of `.prompt.md` companion files when removing extensions via `specify extension remove`
|
||||||
|
- Fixed a syntax regression in `src/specify_cli/__init__.py` in `_build_ai_assistant_help()` that broke `ruff` and `pytest` collection in CI.
|
||||||
|
## [0.1.12] - 2026-03-02
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Improved agent selector UI with cyan highlighting for agent keys and gray parentheses for full names
|
- fix: use RELEASE_PAT so tag push triggers release workflow (#1736)
|
||||||
|
- fix: release-trigger uses release branch + PR instead of direct push to main (#1733)
|
||||||
|
- fix: Split release process to sync pyproject.toml version with git tags (#1732)
|
||||||
|
|
||||||
## [0.0.8] - 2025-09-19
|
|
||||||
|
|
||||||
### Added
|
## [0.1.10] - 2026-03-02
|
||||||
|
|
||||||
- Windsurf IDE support as additional AI assistant option (thank you [@raedkit](https://github.com/raedkit) for the work in [#151](https://github.com/github/spec-kit/pull/151))
|
|
||||||
- GitHub token support for API requests to handle corporate environments and rate limiting (contributed by [@zryfish](https://github.com/@zryfish) in [#243](https://github.com/github/spec-kit/pull/243))
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Updated README with Windsurf examples and GitHub token usage
|
|
||||||
- Enhanced release workflow to include Windsurf templates
|
|
||||||
|
|
||||||
## [0.0.7] - 2025-09-18
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- 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
|
|
||||||
|
|
||||||
- opencode support as additional AI assistant option
|
|
||||||
|
|
||||||
## [0.0.5] - 2025-09-17
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Qwen Code support as additional AI assistant option
|
|
||||||
|
|
||||||
## [0.0.4] - 2025-09-14
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- SOCKS proxy support for corporate environments via `httpx[socks]` dependency
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
N/A
|
- **Version Sync Issue (#1721)**: Fixed version mismatch between `pyproject.toml` and git release tags
|
||||||
|
- Split release process into two workflows: `release-trigger.yml` for version management and `release.yml` for artifact building
|
||||||
|
- Version bump now happens BEFORE tag creation, ensuring tags point to commits with correct version
|
||||||
|
- Supports both manual version specification and auto-increment (patch version)
|
||||||
|
- Git tags now accurately reflect the version in `pyproject.toml` at that commit
|
||||||
|
- Prevents confusion when installing from source
|
||||||
|
|
||||||
|
## [0.1.9] - 2026-02-28
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
N/A
|
- Updated dependency: bumped astral-sh/setup-uv from 6 to 7
|
||||||
|
|
||||||
|
## [0.1.8] - 2026-02-28
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated dependency: bumped actions/setup-python from 5 to 6
|
||||||
|
|
||||||
|
## [0.1.7] - 2026-02-27
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated outdated GitHub Actions versions
|
||||||
|
- Documented dual-catalog system for extensions
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed version command in documentation
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added Cleanup Extension to README
|
||||||
|
- Added retrospective extension to community catalog
|
||||||
|
|
||||||
|
## [0.1.6] - 2026-02-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Parameter Ordering Issues (#1641)**: Fixed CLI parameter parsing issue where option flags were incorrectly consumed as values for preceding options
|
||||||
|
- Added validation to detect when `--ai` or `--ai-commands-dir` incorrectly consume following flags like `--here` or `--ai-skills`
|
||||||
|
- Now provides clear error messages: "Invalid value for --ai: '--here'"
|
||||||
|
- Includes helpful hints suggesting proper usage and listing available agents
|
||||||
|
- Commands like `specify init --ai-skills --ai --here` now fail with actionable feedback instead of confusing "Must specify project name" errors
|
||||||
|
- Added comprehensive test suite (5 new tests) to prevent regressions
|
||||||
|
|
||||||
|
## [0.1.5] - 2026-02-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **AI Skills Installation Bug (#1658)**: Fixed `--ai-skills` flag not generating skill files for GitHub Copilot and other agents with non-standard command directory structures
|
||||||
|
- Added `commands_subdir` field to `AGENT_CONFIG` to explicitly specify the subdirectory name for each agent
|
||||||
|
- Affected agents now work correctly: copilot (`.github/agents/`), opencode (`.opencode/command/`), windsurf (`.windsurf/workflows/`), codex (`.codex/prompts/`), kilocode (`.kilocode/workflows/`), q (`.amazonq/prompts/`), and agy (`.agent/workflows/`)
|
||||||
|
- The `install_ai_skills()` function now uses the correct path for all agents instead of assuming `commands/` for everyone
|
||||||
|
|
||||||
|
## [0.1.4] - 2026-02-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Qoder CLI detection**: Renamed `AGENT_CONFIG` key from `"qoder"` to `"qodercli"` to match the actual executable name, fixing `specify check` and `specify init --ai` detection failures
|
||||||
|
|
||||||
|
## [0.1.3] - 2026-02-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Generic Agent Support**: Added `--ai generic` option for unsupported AI agents ("bring your own agent")
|
||||||
|
- Requires `--ai-commands-dir <path>` to specify where the agent reads commands from
|
||||||
|
- Generates Markdown commands with `$ARGUMENTS` format (compatible with most agents)
|
||||||
|
- Example: `specify init my-project --ai generic --ai-commands-dir .myagent/commands/`
|
||||||
|
- Enables users to start with Spec Kit immediately while their agent awaits formal support
|
||||||
|
|
||||||
|
## [0.0.102] - 2026-02-20
|
||||||
|
|
||||||
|
- fix: include 'src/**' path in release workflow triggers (#1646)
|
||||||
|
|
||||||
|
## [0.0.101] - 2026-02-19
|
||||||
|
|
||||||
|
- chore(deps): bump github/codeql-action from 3 to 4 (#1635)
|
||||||
|
|
||||||
|
## [0.0.100] - 2026-02-19
|
||||||
|
|
||||||
|
- Add pytest and Python linting (ruff) to CI (#1637)
|
||||||
|
- feat: add pull request template for better contribution guidelines (#1634)
|
||||||
|
|
||||||
|
## [0.0.99] - 2026-02-19
|
||||||
|
|
||||||
|
- Feat/ai skills (#1632)
|
||||||
|
|
||||||
|
## [0.0.98] - 2026-02-19
|
||||||
|
|
||||||
|
- chore(deps): bump actions/stale from 9 to 10 (#1623)
|
||||||
|
- feat: add dependabot configuration for pip and GitHub Actions updates (#1622)
|
||||||
|
|
||||||
|
## [0.0.97] - 2026-02-18
|
||||||
|
|
||||||
|
- Remove Maintainers section from README.md (#1618)
|
||||||
|
|
||||||
|
## [0.0.96] - 2026-02-17
|
||||||
|
|
||||||
|
- fix: typo in plan-template.md (#1446)
|
||||||
|
|
||||||
|
## [0.0.95] - 2026-02-12
|
||||||
|
|
||||||
|
- Feat: add a new agent: Google Anti Gravity (#1220)
|
||||||
|
|
||||||
|
## [0.0.94] - 2026-02-11
|
||||||
|
|
||||||
|
- Add stale workflow for 180-day inactive issues and PRs (#1594)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ include:
|
|||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
advances
|
advances
|
||||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
- Public or private harassment
|
- Public or private harassment
|
||||||
- Publishing others' private information, such as a physical or electronic
|
- Publishing others' private information, such as a physical or electronic
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ On [GitHub Codespaces](https://github.com/features/codespaces) it's even simpler
|
|||||||
|
|
||||||
## Submitting a pull request
|
## Submitting a pull request
|
||||||
|
|
||||||
>[!NOTE]
|
> [!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.
|
> 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. Fork and clone the repository
|
||||||
1. Configure and install the dependencies: `uv sync`
|
1. Configure and install the dependencies: `uv sync`
|
||||||
@@ -62,6 +62,29 @@ When working on spec-kit:
|
|||||||
3. Test script functionality in the `scripts/` directory
|
3. Test script functionality in the `scripts/` directory
|
||||||
4. Ensure memory files (`memory/constitution.md`) are updated if major process changes are made
|
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
|
## AI contributions in Spec Kit
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
|
|||||||
195
README.md
195
README.md
@@ -1,5 +1,5 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="./media/logo_small.webp" alt="Spec Kit Logo"/>
|
<img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
|
||||||
<h1>🌱 Spec Kit</h1>
|
<h1>🌱 Spec Kit</h1>
|
||||||
<h3><em>Build high-quality software faster.</em></h3>
|
<h3><em>Build high-quality software faster.</em></h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
||||||
- [⚡ Get Started](#-get-started)
|
- [⚡ Get Started](#-get-started)
|
||||||
- [📽️ Video Overview](#️-video-overview)
|
- [📽️ Video Overview](#️-video-overview)
|
||||||
|
- [🚶 Community Walkthroughs](#-community-walkthroughs)
|
||||||
- [🤖 Supported AI Agents](#-supported-ai-agents)
|
- [🤖 Supported AI Agents](#-supported-ai-agents)
|
||||||
- [🔧 Specify CLI Reference](#-specify-cli-reference)
|
- [🔧 Specify CLI Reference](#-specify-cli-reference)
|
||||||
- [📚 Core Philosophy](#-core-philosophy)
|
- [📚 Core Philosophy](#-core-philosophy)
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
- [📖 Learn More](#-learn-more)
|
- [📖 Learn More](#-learn-more)
|
||||||
- [📋 Detailed Process](#-detailed-process)
|
- [📋 Detailed Process](#-detailed-process)
|
||||||
- [🔍 Troubleshooting](#-troubleshooting)
|
- [🔍 Troubleshooting](#-troubleshooting)
|
||||||
- [👥 Maintainers](#-maintainers)
|
|
||||||
- [💬 Support](#-support)
|
- [💬 Support](#-support)
|
||||||
- [🙏 Acknowledgements](#-acknowledgements)
|
- [🙏 Acknowledgements](#-acknowledgements)
|
||||||
- [📄 License](#-license)
|
- [📄 License](#-license)
|
||||||
@@ -57,11 +57,19 @@ uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
|
|||||||
Then use the tool directly:
|
Then use the tool directly:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Create new project
|
||||||
specify init <PROJECT_NAME>
|
specify init <PROJECT_NAME>
|
||||||
|
|
||||||
|
# Or initialize in existing project
|
||||||
|
specify init . --ai claude
|
||||||
|
# or
|
||||||
|
specify init --here --ai claude
|
||||||
|
|
||||||
|
# Check installed tools
|
||||||
specify check
|
specify check
|
||||||
```
|
```
|
||||||
|
|
||||||
To upgrade specify run:
|
To upgrade Specify, see the [Upgrade Guide](./docs/upgrade.md) for detailed instructions. Quick upgrade:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
|
uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git
|
||||||
@@ -72,7 +80,13 @@ uv tool install specify-cli --force --from git+https://github.com/github/spec-ki
|
|||||||
Run directly without installing:
|
Run directly without installing:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Create new project
|
||||||
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
|
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
|
||||||
|
|
||||||
|
# Or initialize in existing project
|
||||||
|
uvx --from git+https://github.com/github/spec-kit.git specify init . --ai claude
|
||||||
|
# or
|
||||||
|
uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai claude
|
||||||
```
|
```
|
||||||
|
|
||||||
**Benefits of persistent installation:**
|
**Benefits of persistent installation:**
|
||||||
@@ -132,24 +146,45 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c
|
|||||||
|
|
||||||
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
||||||
|
|
||||||
|
## 🚶 Community Walkthroughs
|
||||||
|
|
||||||
|
See Spec-Driven Development in action across different scenarios with these community-contributed walkthroughs:
|
||||||
|
|
||||||
|
- **[Greenfield .NET CLI tool](https://github.com/mnriem/spec-kit-dotnet-cli-demo)** — Builds a Timezone Utility as a .NET single-binary CLI tool from a blank directory, covering the full spec-kit workflow: constitution, specify, plan, tasks, and multi-pass implement using GitHub Copilot agents.
|
||||||
|
|
||||||
|
- **[Greenfield Spring Boot + React platform](https://github.com/mnriem/spec-kit-spring-react-demo)** — Builds an LLM performance analytics platform (REST API, graphs, iteration tracking) from scratch using Spring Boot, embedded React, PostgreSQL, and Docker Compose, with a clarify step and a cross-artifact consistency analysis pass included.
|
||||||
|
|
||||||
|
- **[Brownfield ASP.NET CMS extension](https://github.com/mnriem/spec-kit-aspnet-brownfield-demo)** — Extends an existing open-source .NET CMS (CarrotCakeCMS-Core, ~307,000 lines of C#, Razor, SQL, JavaScript, and config files) with two new features — cross-platform Docker Compose infrastructure and a token-authenticated headless REST API — demonstrating how spec-kit fits into existing codebases without prior specs or a constitution.
|
||||||
|
|
||||||
|
- **[Brownfield Java runtime extension](https://github.com/mnriem/spec-kit-java-brownfield-demo)** — Extends an existing open-source Jakarta EE runtime (Piranha, ~420,000 lines of Java, XML, JSP, HTML, and config files across 180 Maven modules) with a password-protected Server Admin Console, demonstrating spec-kit on a large multi-module Java project with no prior specs or constitution.
|
||||||
|
|
||||||
## 🤖 Supported AI Agents
|
## 🤖 Supported AI Agents
|
||||||
|
|
||||||
| Agent | Support | Notes |
|
| Agent | Support | Notes |
|
||||||
|-----------------------------------------------------------|---------|---------------------------------------------------|
|
| ------------------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [Claude Code](https://www.anthropic.com/claude-code) | ✅ | |
|
| [Qoder CLI](https://qoder.com/cli) | ✅ | |
|
||||||
| [GitHub Copilot](https://code.visualstudio.com/) | ✅ | |
|
| [Kiro CLI](https://kiro.dev/docs/cli/) | ✅ | Use `--ai kiro-cli` (alias: `--ai kiro`) |
|
||||||
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | ✅ | |
|
| [Amp](https://ampcode.com/) | ✅ | |
|
||||||
| [Cursor](https://cursor.sh/) | ✅ | |
|
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | ✅ | |
|
||||||
| [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | |
|
| [Claude Code](https://www.anthropic.com/claude-code) | ✅ | |
|
||||||
| [opencode](https://opencode.ai/) | ✅ | |
|
| [CodeBuddy CLI](https://www.codebuddy.ai/cli) | ✅ | |
|
||||||
| [Windsurf](https://windsurf.com/) | ✅ | |
|
| [Codex CLI](https://github.com/openai/codex) | ✅ | |
|
||||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
|
| [Cursor](https://cursor.sh/) | ✅ | |
|
||||||
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | ✅ | |
|
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | ✅ | |
|
||||||
| [CodeBuddy CLI](https://www.codebuddy.ai/cli) | ✅ | |
|
| [GitHub Copilot](https://code.visualstudio.com/) | ✅ | |
|
||||||
| [Roo Code](https://roocode.com/) | ✅ | |
|
| [IBM Bob](https://www.ibm.com/products/bob) | ✅ | IDE-based agent with slash command support |
|
||||||
| [Codex CLI](https://github.com/openai/codex) | ✅ | |
|
| [Jules](https://jules.google.com/) | ✅ | |
|
||||||
| [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. |
|
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
|
||||||
| [Amp](https://ampcode.com/) | ✅ | |
|
| [opencode](https://opencode.ai/) | ✅ | |
|
||||||
|
| [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | |
|
||||||
|
| [Roo Code](https://roocode.com/) | ✅ | |
|
||||||
|
| [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | |
|
||||||
|
| [Tabnine CLI](https://docs.tabnine.com/main/getting-started/tabnine-cli) | ✅ | |
|
||||||
|
| [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | ✅ | |
|
||||||
|
| [Kimi Code](https://code.kimi.com/) | ✅ | |
|
||||||
|
| [Windsurf](https://windsurf.com/) | ✅ | |
|
||||||
|
| [Antigravity (agy)](https://antigravity.google/) | ✅ | Requires `--ai-skills` |
|
||||||
|
| Generic | ✅ | Bring your own agent — use `--ai generic --ai-commands-dir <path>` for unsupported agents |
|
||||||
|
|
||||||
## 🔧 Specify CLI Reference
|
## 🔧 Specify CLI Reference
|
||||||
|
|
||||||
@@ -157,25 +192,27 @@ The `specify` command supports the following options:
|
|||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|-------------|----------------------------------------------------------------|
|
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `init` | Initialize a new Specify project from the latest template |
|
| `init` | Initialize a new Specify project from the latest template |
|
||||||
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`) |
|
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`, `kiro-cli`, `shai`, `qodercli`, `vibe`, `kimi`) |
|
||||||
|
|
||||||
### `specify init` Arguments & Options
|
### `specify init` Arguments & Options
|
||||||
|
|
||||||
| Argument/Option | Type | Description |
|
| Argument/Option | Type | Description |
|
||||||
|------------------------|----------|------------------------------------------------------------------------------|
|
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `<project-name>` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) |
|
| `<project-name>` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) |
|
||||||
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, or `q` |
|
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `kiro-cli` (`kiro` alias), `agy`, `bob`, `qodercli`, `vibe`, `kimi`, or `generic` (requires `--ai-commands-dir`) |
|
||||||
| `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) |
|
| `--ai-commands-dir` | Option | Directory for agent command files (required with `--ai generic`, e.g. `.myagent/commands/`) |
|
||||||
| `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code |
|
| `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||||
| `--no-git` | Flag | Skip git repository initialization |
|
| `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code |
|
||||||
| `--here` | Flag | Initialize project in the current directory instead of creating a new one |
|
| `--no-git` | Flag | Skip git repository initialization |
|
||||||
| `--force` | Flag | Force merge/overwrite when initializing in current directory (skip confirmation) |
|
| `--here` | Flag | Initialize project in the current directory instead of creating a new one |
|
||||||
| `--skip-tls` | Flag | Skip SSL/TLS verification (not recommended) |
|
| `--force` | Flag | Force merge/overwrite when initializing in current directory (skip confirmation) |
|
||||||
| `--debug` | Flag | Enable detailed debug output for troubleshooting |
|
| `--skip-tls` | Flag | Skip SSL/TLS verification (not recommended) |
|
||||||
| `--github-token` | Option | GitHub token for API requests (or set GH_TOKEN/GITHUB_TOKEN env variable) |
|
| `--debug` | Flag | Enable detailed debug output for troubleshooting |
|
||||||
|
| `--github-token` | Option | GitHub token for API requests (or set GH_TOKEN/GITHUB_TOKEN env variable) |
|
||||||
|
| `--ai-skills` | Flag | Install Prompt.MD templates as agent skills in agent-specific `skills/` directory (requires `--ai`) |
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -189,12 +226,33 @@ specify init my-project --ai claude
|
|||||||
# Initialize with Cursor support
|
# Initialize with Cursor support
|
||||||
specify init my-project --ai cursor-agent
|
specify init my-project --ai cursor-agent
|
||||||
|
|
||||||
|
# Initialize with Qoder support
|
||||||
|
specify init my-project --ai qodercli
|
||||||
|
|
||||||
# Initialize with Windsurf support
|
# Initialize with Windsurf support
|
||||||
specify init my-project --ai windsurf
|
specify init my-project --ai windsurf
|
||||||
|
|
||||||
|
# Initialize with Kiro CLI support
|
||||||
|
specify init my-project --ai kiro-cli
|
||||||
|
|
||||||
# Initialize with Amp support
|
# Initialize with Amp support
|
||||||
specify init my-project --ai amp
|
specify init my-project --ai amp
|
||||||
|
|
||||||
|
# Initialize with SHAI support
|
||||||
|
specify init my-project --ai shai
|
||||||
|
|
||||||
|
# Initialize with Mistral Vibe support
|
||||||
|
specify init my-project --ai vibe
|
||||||
|
|
||||||
|
# Initialize with IBM Bob support
|
||||||
|
specify init my-project --ai bob
|
||||||
|
|
||||||
|
# Initialize with Antigravity support
|
||||||
|
specify init my-project --ai agy --ai-skills
|
||||||
|
|
||||||
|
# Initialize with an unsupported agent (generic / bring your own agent)
|
||||||
|
specify init my-project --ai generic --ai-commands-dir .myagent/commands/
|
||||||
|
|
||||||
# Initialize with PowerShell scripts (Windows/cross-platform)
|
# Initialize with PowerShell scripts (Windows/cross-platform)
|
||||||
specify init my-project --ai copilot --script ps
|
specify init my-project --ai copilot --script ps
|
||||||
|
|
||||||
@@ -217,6 +275,12 @@ specify init my-project --ai claude --debug
|
|||||||
# Use GitHub token for API requests (helpful for corporate environments)
|
# Use GitHub token for API requests (helpful for corporate environments)
|
||||||
specify init my-project --ai claude --github-token ghp_your_token_here
|
specify init my-project --ai claude --github-token ghp_your_token_here
|
||||||
|
|
||||||
|
# Install agent skills with the project
|
||||||
|
specify init my-project --ai claude --ai-skills
|
||||||
|
|
||||||
|
# Initialize in current directory with agent skills
|
||||||
|
specify init --here --ai gemini --ai-skills
|
||||||
|
|
||||||
# Check system requirements
|
# Check system requirements
|
||||||
specify check
|
specify check
|
||||||
```
|
```
|
||||||
@@ -229,29 +293,29 @@ After running `specify init`, your AI coding agent will have access to these sla
|
|||||||
|
|
||||||
Essential commands for the Spec-Driven Development workflow:
|
Essential commands for the Spec-Driven Development workflow:
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|--------------------------|-----------------------------------------------------------------------|
|
| ----------------------- | ------------------------------------------------------------------------ |
|
||||||
| `/speckit.constitution` | Create or update project governing principles and development guidelines |
|
| `/speckit.constitution` | Create or update project governing principles and development guidelines |
|
||||||
| `/speckit.specify` | Define what you want to build (requirements and user stories) |
|
| `/speckit.specify` | Define what you want to build (requirements and user stories) |
|
||||||
| `/speckit.plan` | Create technical implementation plans with your chosen tech stack |
|
| `/speckit.plan` | Create technical implementation plans with your chosen tech stack |
|
||||||
| `/speckit.tasks` | Generate actionable task lists for implementation |
|
| `/speckit.tasks` | Generate actionable task lists for implementation |
|
||||||
| `/speckit.implement` | Execute all tasks to build the feature according to the plan |
|
| `/speckit.implement` | Execute all tasks to build the feature according to the plan |
|
||||||
|
|
||||||
#### Optional Commands
|
#### Optional Commands
|
||||||
|
|
||||||
Additional commands for enhanced quality and validation:
|
Additional commands for enhanced quality and validation:
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|----------------------|-----------------------------------------------------------------------|
|
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `/speckit.clarify` | Clarify underspecified areas (recommended before `/speckit.plan`; formerly `/quizme`) |
|
| `/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.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") |
|
| `/speckit.checklist` | Generate custom quality checklists that validate requirements completeness, clarity, and consistency (like "unit tests for English") |
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
||||||
| Variable | Description |
|
| 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. |
|
| `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
|
||||||
|
|
||||||
@@ -264,11 +328,11 @@ Spec-Driven Development is a structured process that emphasizes:
|
|||||||
|
|
||||||
## 🌟 Development Phases
|
## 🌟 Development Phases
|
||||||
|
|
||||||
| Phase | Focus | Key Activities |
|
| 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> |
|
| **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> |
|
| **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> |
|
| **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
|
||||||
|
|
||||||
@@ -360,7 +424,7 @@ specify init . --force --ai claude
|
|||||||
specify init --here --force --ai claude
|
specify init --here --force --ai claude
|
||||||
```
|
```
|
||||||
|
|
||||||
The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, 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, Tabnine CLI, Kiro CLI, or Mistral Vibe installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
specify init <project_name> --ai claude --ignore-agent-tools
|
specify init <project_name> --ai claude --ignore-agent-tools
|
||||||
@@ -386,8 +450,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.
|
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]
|
> [!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**.
|
> 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:
|
An example prompt:
|
||||||
|
|
||||||
@@ -530,8 +594,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.
|
That's way too untargeted research. The research needs to help you solve a specific targeted question.
|
||||||
```
|
```
|
||||||
|
|
||||||
>[!NOTE]
|
> [!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.
|
> 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
|
### **STEP 5:** Have Claude Code validate the plan
|
||||||
|
|
||||||
@@ -549,8 +613,8 @@ 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.
|
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]
|
> [!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.
|
> 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:** Generate task breakdown with /speckit.tasks
|
### **STEP 6:** Generate task breakdown with /speckit.tasks
|
||||||
|
|
||||||
@@ -587,8 +651,8 @@ The `/speckit.implement` command will:
|
|||||||
- Follow the TDD approach defined in your task plan
|
- Follow the TDD approach defined in your task plan
|
||||||
- Provide progress updates and handle errors appropriately
|
- Provide progress updates and handle errors appropriately
|
||||||
|
|
||||||
>[!IMPORTANT]
|
> [!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.
|
> 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.
|
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.
|
||||||
|
|
||||||
@@ -615,11 +679,6 @@ echo "Cleaning up..."
|
|||||||
rm gcm-linux_amd64.2.6.1.deb
|
rm gcm-linux_amd64.2.6.1.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
## 👥 Maintainers
|
|
||||||
|
|
||||||
- Den Delimarsky ([@localden](https://github.com/localden))
|
|
||||||
- John Lam ([@jflam](https://github.com/jflam))
|
|
||||||
|
|
||||||
## 💬 Support
|
## 💬 Support
|
||||||
|
|
||||||
For support, please open a [GitHub issue](https://github.com/github/spec-kit/issues/new). We welcome bug reports, feature requests, and questions about using Spec-Driven Development.
|
For support, please open a [GitHub issue](https://github.com/github/spec-kit/issues/new). We welcome bug reports, feature requests, and questions about using Spec-Driven Development.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Spec-Driven Development **flips the script** on traditional software development
|
|||||||
|
|
||||||
- [Installation Guide](installation.md)
|
- [Installation Guide](installation.md)
|
||||||
- [Quick Start Guide](quickstart.md)
|
- [Quick Start Guide](quickstart.md)
|
||||||
|
- [Upgrade Guide](upgrade.md)
|
||||||
- [Local Development](local-development.md)
|
- [Local Development](local-development.md)
|
||||||
|
|
||||||
## Core Philosophy
|
## Core Philosophy
|
||||||
|
|||||||
@@ -2,16 +2,24 @@
|
|||||||
|
|
||||||
This guide will help you get started with Spec-Driven Development using Spec Kit.
|
This guide will help you get started with Spec-Driven Development using Spec Kit.
|
||||||
|
|
||||||
> 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
|
```bash
|
||||||
|
# Create a new project directory
|
||||||
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
|
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME>
|
||||||
|
|
||||||
|
# OR initialize in the current directory
|
||||||
|
uvx --from git+https://github.com/github/spec-kit.git specify init .
|
||||||
```
|
```
|
||||||
|
|
||||||
Pick script type explicitly (optional):
|
Pick script type explicitly (optional):
|
||||||
@@ -21,31 +29,74 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME
|
|||||||
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script sh # Force POSIX shell
|
uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME> --script sh # Force POSIX shell
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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.
|
/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
|
```bash
|
||||||
|
/speckit.clarify Focus on security and performance requirements.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Create a Technical Implementation Plan
|
||||||
|
|
||||||
|
**In the chat**, use the `/speckit.plan` slash command to provide your tech stack and architecture choices.
|
||||||
|
|
||||||
|
```markdown
|
||||||
/speckit.plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database.
|
/speckit.plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database.
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> **Phased Implementation**: For complex projects, implement in phases to avoid overwhelming the agent's context. Start with core functionality, validate it works, then add features incrementally.
|
||||||
|
|
||||||
## Detailed Example: Building Taskify
|
## Detailed Example: Building Taskify
|
||||||
|
|
||||||
Here's a complete example of building a team productivity platform:
|
Here's a complete example of building a team productivity platform:
|
||||||
|
|
||||||
### Step 1: Define 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
|
```text
|
||||||
Develop Taskify, a team productivity platform. It should allow users to create projects, add team members,
|
Develop Taskify, a team productivity platform. It should allow users to create projects, add team members,
|
||||||
@@ -54,60 +105,64 @@ let's call it "Create Taskify," let's have multiple users but the users will be
|
|||||||
I want five users in two different categories, one product manager and four engineers. Let's create three
|
I want five users in two different categories, one product manager and four engineers. Let's create three
|
||||||
different sample projects. Let's have the standard Kanban columns for the status of each task, such as "To Do,"
|
different sample projects. Let's have the standard Kanban columns for the status of each task, such as "To Do,"
|
||||||
"In Progress," "In Review," and "Done." There will be no login for this application as this is just the very
|
"In Progress," "In Review," and "Done." There will be no login for this application as this is just the very
|
||||||
first testing thing to ensure that our basic features are set up. For each task in the UI for a task card,
|
first testing thing to ensure that our basic features are set up.
|
||||||
you should be able to change the current status of the task between the different columns in the Kanban work board.
|
|
||||||
You should be able to leave an unlimited number of comments for a particular card. You should be able to, from that task
|
|
||||||
card, assign one of the valid users. When you first launch Taskify, it's going to give you a list of the five users to pick
|
|
||||||
from. There will be no password required. When you click on a user, you go into the main view, which displays the list of
|
|
||||||
projects. When you click on a project, you open the Kanban board for that project. You're going to see the columns.
|
|
||||||
You'll be able to drag and drop cards back and forth between different columns. You will see any cards that are
|
|
||||||
assigned to you, the currently logged in user, in a different color from all the other ones, so you can quickly
|
|
||||||
see yours. You can edit any comments that you make, but you can't edit comments that other people made. You can
|
|
||||||
delete any comments that you made, but you can't delete comments anybody else made.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 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
|
```bash
|
||||||
For each sample project or project that you create there should be a variable number of tasks between 5 and 15
|
/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.
|
||||||
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.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Also validate the specification checklist:
|
You can continue to refine the spec with more details using `/speckit.clarify`:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
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.
|
/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:
|
Be specific about your tech stack and technical requirements:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
We are going to generate this using .NET Aspire, using Postgres as the database. The frontend should use
|
/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.
|
||||||
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: Define Tasks
|
||||||
|
|
||||||
Have your AI agent audit the implementation plan:
|
Generate an actionable task list using the `/speckit.tasks` command:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
Now I want you to go and audit the implementation plan and the implementation detail files.
|
/speckit.tasks
|
||||||
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.
|
|
||||||
|
### Step 7: Validate and Implement
|
||||||
|
|
||||||
|
Have your AI agent audit the implementation plan using `/speckit.analyze`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/speckit.analyze
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, implement the solution:
|
Finally, implement the solution:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
implement specs/002-create-taskify/plan.md
|
/speckit.implement
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> **Phased Implementation**: For large projects like Taskify, consider implementing in phases (e.g., Phase 1: Basic project/task structure, Phase 2: Kanban functionality, Phase 3: Comments and assignments). This prevents context saturation and allows for validation at each stage.
|
||||||
|
|
||||||
## Key Principles
|
## Key Principles
|
||||||
|
|
||||||
- **Be explicit** about what you're building and why
|
- **Be explicit** about what you're building and why
|
||||||
@@ -118,6 +173,6 @@ implement specs/002-create-taskify/plan.md
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
- Read the complete methodology for in-depth guidance
|
- Read the [complete methodology](https://github.com/github/spec-kit/blob/main/spec-driven.md) for in-depth guidance
|
||||||
- Check out more examples in the repository
|
- Check out [more examples](https://github.com/github/spec-kit/tree/main/templates) in the repository
|
||||||
- Explore the source code on GitHub
|
- Explore the [source code on GitHub](https://github.com/github/spec-kit)
|
||||||
|
|||||||
@@ -9,10 +9,11 @@
|
|||||||
href: installation.md
|
href: installation.md
|
||||||
- name: Quick Start
|
- name: Quick Start
|
||||||
href: quickstart.md
|
href: quickstart.md
|
||||||
|
- name: Upgrade
|
||||||
|
href: upgrade.md
|
||||||
|
|
||||||
# Development workflows
|
# Development workflows
|
||||||
- name: Development
|
- name: Development
|
||||||
items:
|
items:
|
||||||
- name: Local Development
|
- name: Local Development
|
||||||
href: local-development.md
|
href: local-development.md
|
||||||
|
|
||||||
|
|||||||
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
|
||||||
816
extensions/EXTENSION-API-REFERENCE.md
Normal file
816
extensions/EXTENSION-API-REFERENCE.md
Normal file
@@ -0,0 +1,816 @@
|
|||||||
|
# Extension API Reference
|
||||||
|
|
||||||
|
Technical reference for Spec Kit extension system APIs and manifest schema.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Extension Manifest](#extension-manifest)
|
||||||
|
2. [Python API](#python-api)
|
||||||
|
3. [Command File Format](#command-file-format)
|
||||||
|
4. [Configuration Schema](#configuration-schema)
|
||||||
|
5. [Hook System](#hook-system)
|
||||||
|
6. [CLI Commands](#cli-commands)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extension Manifest
|
||||||
|
|
||||||
|
### Schema Version 1.0
|
||||||
|
|
||||||
|
File: `extension.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: "1.0" # Required
|
||||||
|
|
||||||
|
extension:
|
||||||
|
id: string # Required, pattern: ^[a-z0-9-]+$
|
||||||
|
name: string # Required, human-readable name
|
||||||
|
version: string # Required, semantic version (X.Y.Z)
|
||||||
|
description: string # Required, brief description (<200 chars)
|
||||||
|
author: string # Required
|
||||||
|
repository: string # Required, valid URL
|
||||||
|
license: string # Required (e.g., "MIT", "Apache-2.0")
|
||||||
|
homepage: string # Optional, valid URL
|
||||||
|
|
||||||
|
requires:
|
||||||
|
speckit_version: string # Required, version specifier (>=X.Y.Z)
|
||||||
|
tools: # Optional, array of tool requirements
|
||||||
|
- name: string # Tool name
|
||||||
|
version: string # Optional, version specifier
|
||||||
|
required: boolean # Optional, default: false
|
||||||
|
|
||||||
|
provides:
|
||||||
|
commands: # Required, at least one command
|
||||||
|
- name: string # Required, pattern: ^speckit\.[a-z0-9-]+\.[a-z0-9-]+$
|
||||||
|
file: string # Required, relative path to command file
|
||||||
|
description: string # Required
|
||||||
|
aliases: [string] # Optional, array of alternate names
|
||||||
|
|
||||||
|
config: # Optional, array of config files
|
||||||
|
- name: string # Config file name
|
||||||
|
template: string # Template file path
|
||||||
|
description: string
|
||||||
|
required: boolean # Default: false
|
||||||
|
|
||||||
|
hooks: # Optional, event hooks
|
||||||
|
event_name: # e.g., "after_tasks", "after_implement"
|
||||||
|
command: string # Command to execute
|
||||||
|
optional: boolean # Default: true
|
||||||
|
prompt: string # Prompt text for optional hooks
|
||||||
|
description: string # Hook description
|
||||||
|
condition: string # Optional, condition expression
|
||||||
|
|
||||||
|
tags: # Optional, array of tags (2-10 recommended)
|
||||||
|
- string
|
||||||
|
|
||||||
|
defaults: # Optional, default configuration values
|
||||||
|
key: value # Any YAML structure
|
||||||
|
```
|
||||||
|
|
||||||
|
### Field Specifications
|
||||||
|
|
||||||
|
#### `extension.id`
|
||||||
|
|
||||||
|
- **Type**: string
|
||||||
|
- **Pattern**: `^[a-z0-9-]+$`
|
||||||
|
- **Description**: Unique extension identifier
|
||||||
|
- **Examples**: `jira`, `linear`, `azure-devops`
|
||||||
|
- **Invalid**: `Jira`, `my_extension`, `extension.id`
|
||||||
|
|
||||||
|
#### `extension.version`
|
||||||
|
|
||||||
|
- **Type**: string
|
||||||
|
- **Format**: Semantic versioning (X.Y.Z)
|
||||||
|
- **Description**: Extension version
|
||||||
|
- **Examples**: `1.0.0`, `0.9.5`, `2.1.3`
|
||||||
|
- **Invalid**: `v1.0`, `1.0`, `1.0.0-beta`
|
||||||
|
|
||||||
|
#### `requires.speckit_version`
|
||||||
|
|
||||||
|
- **Type**: string
|
||||||
|
- **Format**: Version specifier
|
||||||
|
- **Description**: Required spec-kit version range
|
||||||
|
- **Examples**:
|
||||||
|
- `>=0.1.0` - Any version 0.1.0 or higher
|
||||||
|
- `>=0.1.0,<2.0.0` - Version 0.1.x or 1.x
|
||||||
|
- `==0.1.0` - Exactly 0.1.0
|
||||||
|
- **Invalid**: `0.1.0`, `>= 0.1.0` (space), `latest`
|
||||||
|
|
||||||
|
#### `provides.commands[].name`
|
||||||
|
|
||||||
|
- **Type**: string
|
||||||
|
- **Pattern**: `^speckit\.[a-z0-9-]+\.[a-z0-9-]+$`
|
||||||
|
- **Description**: Namespaced command name
|
||||||
|
- **Format**: `speckit.{extension-id}.{command-name}`
|
||||||
|
- **Examples**: `speckit.jira.specstoissues`, `speckit.linear.sync`
|
||||||
|
- **Invalid**: `jira.specstoissues`, `speckit.command`, `speckit.jira.CreateIssues`
|
||||||
|
|
||||||
|
#### `hooks`
|
||||||
|
|
||||||
|
- **Type**: object
|
||||||
|
- **Keys**: Event names (e.g., `after_tasks`, `after_implement`, `before_commit`)
|
||||||
|
- **Description**: Hooks that execute at lifecycle events
|
||||||
|
- **Events**: Defined by core spec-kit commands
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Python API
|
||||||
|
|
||||||
|
### ExtensionManifest
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ExtensionManifest
|
||||||
|
|
||||||
|
manifest = ExtensionManifest(Path("extension.yml"))
|
||||||
|
```
|
||||||
|
|
||||||
|
**Properties**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
manifest.id # str: Extension ID
|
||||||
|
manifest.name # str: Extension name
|
||||||
|
manifest.version # str: Version
|
||||||
|
manifest.description # str: Description
|
||||||
|
manifest.requires_speckit_version # str: Required spec-kit version
|
||||||
|
manifest.commands # List[Dict]: Command definitions
|
||||||
|
manifest.hooks # Dict: Hook definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
manifest.get_hash() # str: SHA256 hash of manifest file
|
||||||
|
```
|
||||||
|
|
||||||
|
**Exceptions**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
ValidationError # Invalid manifest structure
|
||||||
|
CompatibilityError # Incompatible with current spec-kit version
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExtensionRegistry
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ExtensionRegistry
|
||||||
|
|
||||||
|
registry = ExtensionRegistry(extensions_dir)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Add extension to registry
|
||||||
|
registry.add(extension_id: str, metadata: dict)
|
||||||
|
|
||||||
|
# Remove extension from registry
|
||||||
|
registry.remove(extension_id: str)
|
||||||
|
|
||||||
|
# Get extension metadata
|
||||||
|
metadata = registry.get(extension_id: str) # Optional[dict]
|
||||||
|
|
||||||
|
# List all extensions
|
||||||
|
extensions = registry.list() # Dict[str, dict]
|
||||||
|
|
||||||
|
# Check if installed
|
||||||
|
is_installed = registry.is_installed(extension_id: str) # bool
|
||||||
|
```
|
||||||
|
|
||||||
|
**Registry Format**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"extensions": {
|
||||||
|
"jira": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"source": "catalog",
|
||||||
|
"manifest_hash": "sha256...",
|
||||||
|
"enabled": true,
|
||||||
|
"registered_commands": ["speckit.jira.specstoissues", ...],
|
||||||
|
"installed_at": "2026-01-28T..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExtensionManager
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ExtensionManager
|
||||||
|
|
||||||
|
manager = ExtensionManager(project_root)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Install from directory
|
||||||
|
manifest = manager.install_from_directory(
|
||||||
|
source_dir: Path,
|
||||||
|
speckit_version: str,
|
||||||
|
register_commands: bool = True
|
||||||
|
) # Returns: ExtensionManifest
|
||||||
|
|
||||||
|
# Install from ZIP
|
||||||
|
manifest = manager.install_from_zip(
|
||||||
|
zip_path: Path,
|
||||||
|
speckit_version: str
|
||||||
|
) # Returns: ExtensionManifest
|
||||||
|
|
||||||
|
# Remove extension
|
||||||
|
success = manager.remove(
|
||||||
|
extension_id: str,
|
||||||
|
keep_config: bool = False
|
||||||
|
) # Returns: bool
|
||||||
|
|
||||||
|
# List installed extensions
|
||||||
|
extensions = manager.list_installed() # List[Dict]
|
||||||
|
|
||||||
|
# Get extension manifest
|
||||||
|
manifest = manager.get_extension(extension_id: str) # Optional[ExtensionManifest]
|
||||||
|
|
||||||
|
# Check compatibility
|
||||||
|
manager.check_compatibility(
|
||||||
|
manifest: ExtensionManifest,
|
||||||
|
speckit_version: str
|
||||||
|
) # Raises: CompatibilityError if incompatible
|
||||||
|
```
|
||||||
|
|
||||||
|
### CatalogEntry
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
Represents a single catalog in the active catalog stack.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import CatalogEntry
|
||||||
|
|
||||||
|
entry = CatalogEntry(
|
||||||
|
url="https://example.com/catalog.json",
|
||||||
|
name="default",
|
||||||
|
priority=1,
|
||||||
|
install_allowed=True,
|
||||||
|
description="Built-in catalog of installable extensions",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fields**:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `url` | `str` | Catalog URL (must use HTTPS, or HTTP for localhost) |
|
||||||
|
| `name` | `str` | Human-readable catalog name |
|
||||||
|
| `priority` | `int` | Sort order (lower = higher priority, wins on conflicts) |
|
||||||
|
| `install_allowed` | `bool` | Whether extensions from this catalog can be installed |
|
||||||
|
| `description` | `str` | Optional human-readable description of the catalog (default: empty) |
|
||||||
|
|
||||||
|
### ExtensionCatalog
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ExtensionCatalog
|
||||||
|
|
||||||
|
catalog = ExtensionCatalog(project_root)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Class attributes**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
ExtensionCatalog.DEFAULT_CATALOG_URL # default catalog URL
|
||||||
|
ExtensionCatalog.COMMUNITY_CATALOG_URL # community catalog URL
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Get the ordered list of active catalogs
|
||||||
|
entries = catalog.get_active_catalogs() # List[CatalogEntry]
|
||||||
|
|
||||||
|
# Fetch catalog (primary catalog, backward compat)
|
||||||
|
catalog_data = catalog.fetch_catalog(force_refresh: bool = False) # Dict
|
||||||
|
|
||||||
|
# Search extensions across all active catalogs
|
||||||
|
# Each result includes _catalog_name and _install_allowed
|
||||||
|
results = catalog.search(
|
||||||
|
query: Optional[str] = None,
|
||||||
|
tag: Optional[str] = None,
|
||||||
|
author: Optional[str] = None,
|
||||||
|
verified_only: bool = False
|
||||||
|
) # Returns: List[Dict] — each dict includes _catalog_name, _install_allowed
|
||||||
|
|
||||||
|
# Get extension info (searches all active catalogs)
|
||||||
|
# Returns None if not found; includes _catalog_name and _install_allowed
|
||||||
|
ext_info = catalog.get_extension_info(extension_id: str) # Optional[Dict]
|
||||||
|
|
||||||
|
# Check cache validity (primary catalog)
|
||||||
|
is_valid = catalog.is_cache_valid() # bool
|
||||||
|
|
||||||
|
# Clear all catalog caches
|
||||||
|
catalog.clear_cache()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result annotation fields**:
|
||||||
|
|
||||||
|
Each extension dict returned by `search()` and `get_extension_info()` includes:
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `_catalog_name` | `str` | Name of the source catalog |
|
||||||
|
| `_install_allowed` | `bool` | Whether installation is allowed from this catalog |
|
||||||
|
|
||||||
|
**Catalog config file** (`.specify/extension-catalogs.yml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
catalogs:
|
||||||
|
- name: "default"
|
||||||
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json"
|
||||||
|
priority: 1
|
||||||
|
install_allowed: true
|
||||||
|
description: "Built-in catalog of installable extensions"
|
||||||
|
- name: "community"
|
||||||
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json"
|
||||||
|
priority: 2
|
||||||
|
install_allowed: false
|
||||||
|
description: "Community-contributed extensions (discovery only)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### HookExecutor
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import HookExecutor
|
||||||
|
|
||||||
|
hook_executor = HookExecutor(project_root)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Get project config
|
||||||
|
config = hook_executor.get_project_config() # Dict
|
||||||
|
|
||||||
|
# Save project config
|
||||||
|
hook_executor.save_project_config(config: Dict)
|
||||||
|
|
||||||
|
# Register hooks
|
||||||
|
hook_executor.register_hooks(manifest: ExtensionManifest)
|
||||||
|
|
||||||
|
# Unregister hooks
|
||||||
|
hook_executor.unregister_hooks(extension_id: str)
|
||||||
|
|
||||||
|
# Get hooks for event
|
||||||
|
hooks = hook_executor.get_hooks_for_event(event_name: str) # List[Dict]
|
||||||
|
|
||||||
|
# Check if hook should execute
|
||||||
|
should_run = hook_executor.should_execute_hook(hook: Dict) # bool
|
||||||
|
|
||||||
|
# Format hook message
|
||||||
|
message = hook_executor.format_hook_message(
|
||||||
|
event_name: str,
|
||||||
|
hooks: List[Dict]
|
||||||
|
) # str
|
||||||
|
```
|
||||||
|
|
||||||
|
### CommandRegistrar
|
||||||
|
|
||||||
|
**Module**: `specify_cli.extensions`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import CommandRegistrar
|
||||||
|
|
||||||
|
registrar = CommandRegistrar()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Methods**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Register commands for Claude Code
|
||||||
|
registered = registrar.register_commands_for_claude(
|
||||||
|
manifest: ExtensionManifest,
|
||||||
|
extension_dir: Path,
|
||||||
|
project_root: Path
|
||||||
|
) # Returns: List[str] (command names)
|
||||||
|
|
||||||
|
# Parse frontmatter
|
||||||
|
frontmatter, body = registrar.parse_frontmatter(content: str)
|
||||||
|
|
||||||
|
# Render frontmatter
|
||||||
|
yaml_text = registrar.render_frontmatter(frontmatter: Dict) # str
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command File Format
|
||||||
|
|
||||||
|
### Universal Command Format
|
||||||
|
|
||||||
|
**File**: `commands/{command-name}.md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
description: "Command description"
|
||||||
|
tools:
|
||||||
|
- 'mcp-server/tool_name'
|
||||||
|
- 'other-mcp-server/other_tool'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Command Title
|
||||||
|
|
||||||
|
Command documentation in Markdown.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Requirement 1
|
||||||
|
2. Requirement 2
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Step 1: Description
|
||||||
|
|
||||||
|
Instruction text...
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
# Shell commands
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Step 2: Another Step
|
||||||
|
|
||||||
|
More instructions...
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
Information about configuration options.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Additional notes and tips.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontmatter Fields
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
description: string # Required, brief command description
|
||||||
|
tools: [string] # Optional, MCP tools required
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Variables
|
||||||
|
|
||||||
|
- `$ARGUMENTS` - Placeholder for user-provided arguments
|
||||||
|
- Extension context automatically injected:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
<!-- Extension: {extension-id} -->
|
||||||
|
<!-- Config: .specify/extensions/{extension-id}/ -->
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Schema
|
||||||
|
|
||||||
|
### Extension Config File
|
||||||
|
|
||||||
|
**File**: `.specify/extensions/{extension-id}/{extension-id}-config.yml`
|
||||||
|
|
||||||
|
Extensions define their own config schema. Common patterns:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Connection settings
|
||||||
|
connection:
|
||||||
|
url: string
|
||||||
|
api_key: string
|
||||||
|
|
||||||
|
# Project settings
|
||||||
|
project:
|
||||||
|
key: string
|
||||||
|
workspace: string
|
||||||
|
|
||||||
|
# Feature flags
|
||||||
|
features:
|
||||||
|
enabled: boolean
|
||||||
|
auto_sync: boolean
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
defaults:
|
||||||
|
labels: [string]
|
||||||
|
assignee: string
|
||||||
|
|
||||||
|
# Custom fields
|
||||||
|
field_mappings:
|
||||||
|
internal_name: "external_field_id"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config Layers
|
||||||
|
|
||||||
|
1. **Extension Defaults** (from `extension.yml` `defaults` section)
|
||||||
|
2. **Project Config** (`{extension-id}-config.yml`)
|
||||||
|
3. **Local Override** (`{extension-id}-config.local.yml`, gitignored)
|
||||||
|
4. **Environment Variables** (`SPECKIT_{EXTENSION}_*`)
|
||||||
|
|
||||||
|
### Environment Variable Pattern
|
||||||
|
|
||||||
|
Format: `SPECKIT_{EXTENSION}_{KEY}`
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `SPECKIT_JIRA_PROJECT_KEY`
|
||||||
|
- `SPECKIT_LINEAR_API_KEY`
|
||||||
|
- `SPECKIT_GITHUB_TOKEN`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hook System
|
||||||
|
|
||||||
|
### Hook Definition
|
||||||
|
|
||||||
|
**In extension.yml**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hooks:
|
||||||
|
after_tasks:
|
||||||
|
command: "speckit.jira.specstoissues"
|
||||||
|
optional: true
|
||||||
|
prompt: "Create Jira issues from tasks?"
|
||||||
|
description: "Automatically create Jira hierarchy"
|
||||||
|
condition: null
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Events
|
||||||
|
|
||||||
|
Standard events (defined by core):
|
||||||
|
|
||||||
|
- `after_tasks` - After task generation
|
||||||
|
- `after_implement` - After implementation
|
||||||
|
- `before_commit` - Before git commit
|
||||||
|
- `after_commit` - After git commit
|
||||||
|
|
||||||
|
### Hook Configuration
|
||||||
|
|
||||||
|
**In `.specify/extensions.yml`**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
hooks:
|
||||||
|
after_tasks:
|
||||||
|
- extension: jira
|
||||||
|
command: speckit.jira.specstoissues
|
||||||
|
enabled: true
|
||||||
|
optional: true
|
||||||
|
prompt: "Create Jira issues from tasks?"
|
||||||
|
description: "..."
|
||||||
|
condition: null
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Message Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Hook**: {extension}
|
||||||
|
Command: `/{command}`
|
||||||
|
Description: {description}
|
||||||
|
|
||||||
|
Prompt: {prompt}
|
||||||
|
To execute: `/{command}`
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for mandatory hooks:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
**Automatic Hook**: {extension}
|
||||||
|
Executing: `/{command}`
|
||||||
|
EXECUTE_COMMAND: {command}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CLI Commands
|
||||||
|
|
||||||
|
### extension list
|
||||||
|
|
||||||
|
**Usage**: `specify extension list [OPTIONS]`
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--available` - Show available extensions from catalog
|
||||||
|
- `--all` - Show both installed and available
|
||||||
|
|
||||||
|
**Output**: List of installed extensions with metadata
|
||||||
|
|
||||||
|
### extension catalog list
|
||||||
|
|
||||||
|
**Usage**: `specify extension catalog list`
|
||||||
|
|
||||||
|
Lists all active catalogs in the current catalog stack, showing name, description, URL, priority, and `install_allowed` status.
|
||||||
|
|
||||||
|
### extension catalog add
|
||||||
|
|
||||||
|
**Usage**: `specify extension catalog add URL [OPTIONS]`
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--name NAME` - Catalog name (required)
|
||||||
|
- `--priority INT` - Priority (lower = higher priority, default: 10)
|
||||||
|
- `--install-allowed / --no-install-allowed` - Allow installs from this catalog (default: false)
|
||||||
|
- `--description TEXT` - Optional description of the catalog
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `URL` - Catalog URL (must use HTTPS)
|
||||||
|
|
||||||
|
Adds a catalog entry to `.specify/extension-catalogs.yml`.
|
||||||
|
|
||||||
|
### extension catalog remove
|
||||||
|
|
||||||
|
**Usage**: `specify extension catalog remove NAME`
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `NAME` - Catalog name to remove
|
||||||
|
|
||||||
|
Removes a catalog entry from `.specify/extension-catalogs.yml`.
|
||||||
|
|
||||||
|
### extension add
|
||||||
|
|
||||||
|
**Usage**: `specify extension add EXTENSION [OPTIONS]`
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--from URL` - Install from custom URL
|
||||||
|
- `--dev PATH` - Install from local directory
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Extension name or URL
|
||||||
|
|
||||||
|
**Note**: Extensions from catalogs with `install_allowed: false` cannot be installed via this command.
|
||||||
|
|
||||||
|
### extension remove
|
||||||
|
|
||||||
|
**Usage**: `specify extension remove EXTENSION [OPTIONS]`
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--keep-config` - Preserve config files
|
||||||
|
- `--force` - Skip confirmation
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Extension ID
|
||||||
|
|
||||||
|
### extension search
|
||||||
|
|
||||||
|
**Usage**: `specify extension search [QUERY] [OPTIONS]`
|
||||||
|
|
||||||
|
Searches all active catalogs simultaneously. Results include source catalog name and install_allowed status.
|
||||||
|
|
||||||
|
**Options**:
|
||||||
|
|
||||||
|
- `--tag TAG` - Filter by tag
|
||||||
|
- `--author AUTHOR` - Filter by author
|
||||||
|
- `--verified` - Show only verified extensions
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `QUERY` - Optional search query
|
||||||
|
|
||||||
|
### extension info
|
||||||
|
|
||||||
|
**Usage**: `specify extension info EXTENSION`
|
||||||
|
|
||||||
|
Shows source catalog and install_allowed status.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Extension ID
|
||||||
|
|
||||||
|
### extension update
|
||||||
|
|
||||||
|
**Usage**: `specify extension update [EXTENSION]`
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Optional, extension ID (default: all)
|
||||||
|
|
||||||
|
### extension enable
|
||||||
|
|
||||||
|
**Usage**: `specify extension enable EXTENSION`
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Extension ID
|
||||||
|
|
||||||
|
### extension disable
|
||||||
|
|
||||||
|
**Usage**: `specify extension disable EXTENSION`
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
- `EXTENSION` - Extension ID
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
### ValidationError
|
||||||
|
|
||||||
|
Raised when extension manifest validation fails.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ValidationError
|
||||||
|
|
||||||
|
try:
|
||||||
|
manifest = ExtensionManifest(path)
|
||||||
|
except ValidationError as e:
|
||||||
|
print(f"Invalid manifest: {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### CompatibilityError
|
||||||
|
|
||||||
|
Raised when extension is incompatible with current spec-kit version.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import CompatibilityError
|
||||||
|
|
||||||
|
try:
|
||||||
|
manager.check_compatibility(manifest, "0.1.0")
|
||||||
|
except CompatibilityError as e:
|
||||||
|
print(f"Incompatible: {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### ExtensionError
|
||||||
|
|
||||||
|
Base exception for all extension-related errors.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import ExtensionError
|
||||||
|
|
||||||
|
try:
|
||||||
|
manager.install_from_directory(path, "0.1.0")
|
||||||
|
except ExtensionError as e:
|
||||||
|
print(f"Extension error: {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version Functions
|
||||||
|
|
||||||
|
### version_satisfies
|
||||||
|
|
||||||
|
Check if a version satisfies a specifier.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from specify_cli.extensions import version_satisfies
|
||||||
|
|
||||||
|
# True if 1.2.3 satisfies >=1.0.0,<2.0.0
|
||||||
|
satisfied = version_satisfies("1.2.3", ">=1.0.0,<2.0.0") # bool
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File System Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
.specify/
|
||||||
|
├── extensions/
|
||||||
|
│ ├── .registry # Extension registry (JSON)
|
||||||
|
│ ├── .cache/ # Catalog cache
|
||||||
|
│ │ ├── catalog.json
|
||||||
|
│ │ └── catalog-metadata.json
|
||||||
|
│ ├── .backup/ # Config backups
|
||||||
|
│ │ └── {ext}-{config}.yml
|
||||||
|
│ ├── {extension-id}/ # Extension directory
|
||||||
|
│ │ ├── extension.yml # Manifest
|
||||||
|
│ │ ├── {ext}-config.yml # User config
|
||||||
|
│ │ ├── {ext}-config.local.yml # Local overrides (gitignored)
|
||||||
|
│ │ ├── {ext}-config.template.yml # Template
|
||||||
|
│ │ ├── commands/ # Command files
|
||||||
|
│ │ │ └── *.md
|
||||||
|
│ │ ├── scripts/ # Helper scripts
|
||||||
|
│ │ │ └── *.sh
|
||||||
|
│ │ ├── docs/ # Documentation
|
||||||
|
│ │ └── README.md
|
||||||
|
│ └── extensions.yml # Project extension config
|
||||||
|
└── scripts/ # (existing spec-kit)
|
||||||
|
|
||||||
|
.claude/
|
||||||
|
└── commands/
|
||||||
|
└── speckit.{ext}.{cmd}.md # Registered commands
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last Updated: 2026-01-28*
|
||||||
|
*API Version: 1.0*
|
||||||
|
*Spec Kit Version: 0.1.0*
|
||||||
712
extensions/EXTENSION-DEVELOPMENT-GUIDE.md
Normal file
712
extensions/EXTENSION-DEVELOPMENT-GUIDE.md
Normal file
@@ -0,0 +1,712 @@
|
|||||||
|
# Extension Development Guide
|
||||||
|
|
||||||
|
A guide for creating Spec Kit extensions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Create Extension Directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir my-extension
|
||||||
|
cd my-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create `extension.yml` Manifest
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
extension:
|
||||||
|
id: "my-ext" # Lowercase, alphanumeric + hyphens only
|
||||||
|
name: "My Extension"
|
||||||
|
version: "1.0.0" # Semantic versioning
|
||||||
|
description: "My custom extension"
|
||||||
|
author: "Your Name"
|
||||||
|
repository: "https://github.com/you/spec-kit-my-ext"
|
||||||
|
license: "MIT"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.1.0" # Minimum spec-kit version
|
||||||
|
tools: # Optional: External tools required
|
||||||
|
- name: "my-tool"
|
||||||
|
required: true
|
||||||
|
version: ">=1.0.0"
|
||||||
|
commands: # Optional: Core commands needed
|
||||||
|
- "speckit.tasks"
|
||||||
|
|
||||||
|
provides:
|
||||||
|
commands:
|
||||||
|
- name: "speckit.my-ext.hello" # Must follow pattern: speckit.{ext-id}.{cmd}
|
||||||
|
file: "commands/hello.md"
|
||||||
|
description: "Say hello"
|
||||||
|
aliases: ["speckit.hello"] # Optional aliases
|
||||||
|
|
||||||
|
config: # Optional: Config files
|
||||||
|
- name: "my-ext-config.yml"
|
||||||
|
template: "my-ext-config.template.yml"
|
||||||
|
description: "Extension configuration"
|
||||||
|
required: false
|
||||||
|
|
||||||
|
hooks: # Optional: Integration hooks
|
||||||
|
after_tasks:
|
||||||
|
command: "speckit.my-ext.hello"
|
||||||
|
optional: true
|
||||||
|
prompt: "Run hello command?"
|
||||||
|
|
||||||
|
tags: # Optional: For catalog search
|
||||||
|
- "example"
|
||||||
|
- "utility"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Create Commands Directory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir commands
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Create Command File
|
||||||
|
|
||||||
|
**File**: `commands/hello.md`
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
description: "Say hello command"
|
||||||
|
tools: # Optional: AI tools this command uses
|
||||||
|
- 'some-tool/function'
|
||||||
|
scripts: # Optional: Helper scripts
|
||||||
|
sh: ../../scripts/bash/helper.sh
|
||||||
|
ps: ../../scripts/powershell/helper.ps1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hello Command
|
||||||
|
|
||||||
|
This command says hello!
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Greet the user
|
||||||
|
2. Show extension is working
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "Hello from my extension!"
|
||||||
|
echo "Arguments: $ARGUMENTS"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extension Configuration
|
||||||
|
|
||||||
|
Load extension config from `.specify/extensions/my-ext/my-ext-config.yml`.
|
||||||
|
|
||||||
|
### 5. Test Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/spec-kit-project
|
||||||
|
specify extension add --dev /path/to/my-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Verify Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension list
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# ✓ My Extension (v1.0.0)
|
||||||
|
# My custom extension
|
||||||
|
# Commands: 1 | Hooks: 1 | Status: Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Test Command
|
||||||
|
|
||||||
|
If using Claude:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude
|
||||||
|
> /speckit.my-ext.hello world
|
||||||
|
```
|
||||||
|
|
||||||
|
The command will be available in `.claude/commands/speckit.my-ext.hello.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Manifest Schema Reference
|
||||||
|
|
||||||
|
### Required Fields
|
||||||
|
|
||||||
|
#### `schema_version`
|
||||||
|
|
||||||
|
Extension manifest schema version. Currently: `"1.0"`
|
||||||
|
|
||||||
|
#### `extension`
|
||||||
|
|
||||||
|
Extension metadata block.
|
||||||
|
|
||||||
|
**Required sub-fields**:
|
||||||
|
|
||||||
|
- `id`: Extension identifier (lowercase, alphanumeric, hyphens)
|
||||||
|
- `name`: Human-readable name
|
||||||
|
- `version`: Semantic version (e.g., "1.0.0")
|
||||||
|
- `description`: Short description
|
||||||
|
|
||||||
|
**Optional sub-fields**:
|
||||||
|
|
||||||
|
- `author`: Extension author
|
||||||
|
- `repository`: Source code URL
|
||||||
|
- `license`: SPDX license identifier
|
||||||
|
- `homepage`: Extension homepage URL
|
||||||
|
|
||||||
|
#### `requires`
|
||||||
|
|
||||||
|
Compatibility requirements.
|
||||||
|
|
||||||
|
**Required sub-fields**:
|
||||||
|
|
||||||
|
- `speckit_version`: Semantic version specifier (e.g., ">=0.1.0,<2.0.0")
|
||||||
|
|
||||||
|
**Optional sub-fields**:
|
||||||
|
|
||||||
|
- `tools`: External tools required (array of tool objects)
|
||||||
|
- `commands`: Core spec-kit commands needed (array of command names)
|
||||||
|
- `scripts`: Core scripts required (array of script names)
|
||||||
|
|
||||||
|
#### `provides`
|
||||||
|
|
||||||
|
What the extension provides.
|
||||||
|
|
||||||
|
**Required sub-fields**:
|
||||||
|
|
||||||
|
- `commands`: Array of command objects (must have at least one)
|
||||||
|
|
||||||
|
**Command object**:
|
||||||
|
|
||||||
|
- `name`: Command name (must match `speckit.{ext-id}.{command}`)
|
||||||
|
- `file`: Path to command file (relative to extension root)
|
||||||
|
- `description`: Command description (optional)
|
||||||
|
- `aliases`: Alternative command names (optional, array)
|
||||||
|
|
||||||
|
### Optional Fields
|
||||||
|
|
||||||
|
#### `hooks`
|
||||||
|
|
||||||
|
Integration hooks for automatic execution.
|
||||||
|
|
||||||
|
Available hook points:
|
||||||
|
|
||||||
|
- `after_tasks`: After `/speckit.tasks` completes
|
||||||
|
- `after_implement`: After `/speckit.implement` completes (future)
|
||||||
|
|
||||||
|
Hook object:
|
||||||
|
|
||||||
|
- `command`: Command to execute (must be in `provides.commands`)
|
||||||
|
- `optional`: If true, prompt user before executing
|
||||||
|
- `prompt`: Prompt text for optional hooks
|
||||||
|
- `description`: Hook description
|
||||||
|
- `condition`: Execution condition (future)
|
||||||
|
|
||||||
|
#### `tags`
|
||||||
|
|
||||||
|
Array of tags for catalog discovery.
|
||||||
|
|
||||||
|
#### `defaults`
|
||||||
|
|
||||||
|
Default extension configuration values.
|
||||||
|
|
||||||
|
#### `config_schema`
|
||||||
|
|
||||||
|
JSON Schema for validating extension configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command File Format
|
||||||
|
|
||||||
|
### Frontmatter (YAML)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
description: "Command description" # Required
|
||||||
|
tools: # Optional
|
||||||
|
- 'tool-name/function'
|
||||||
|
scripts: # Optional
|
||||||
|
sh: ../../scripts/bash/helper.sh
|
||||||
|
ps: ../../scripts/powershell/helper.ps1
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### Body (Markdown)
|
||||||
|
|
||||||
|
Use standard Markdown with special placeholders:
|
||||||
|
|
||||||
|
- `$ARGUMENTS`: User-provided arguments
|
||||||
|
- `{SCRIPT}`: Replaced with script path during registration
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Parse arguments
|
||||||
|
2. Execute logic
|
||||||
|
|
||||||
|
```bash
|
||||||
|
args="$ARGUMENTS"
|
||||||
|
echo "Running with args: $args"
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### Script Path Rewriting
|
||||||
|
|
||||||
|
Extension commands use relative paths that get rewritten during registration:
|
||||||
|
|
||||||
|
**In extension**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
scripts:
|
||||||
|
sh: ../../scripts/bash/helper.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**After registration**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
scripts:
|
||||||
|
sh: .specify/scripts/bash/helper.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows scripts to reference core spec-kit scripts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
### Config Template
|
||||||
|
|
||||||
|
**File**: `my-ext-config.template.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# My Extension Configuration
|
||||||
|
# Copy this to my-ext-config.yml and customize
|
||||||
|
|
||||||
|
# Example configuration
|
||||||
|
api:
|
||||||
|
endpoint: "https://api.example.com"
|
||||||
|
timeout: 30
|
||||||
|
|
||||||
|
features:
|
||||||
|
feature_a: true
|
||||||
|
feature_b: false
|
||||||
|
|
||||||
|
credentials:
|
||||||
|
# DO NOT commit credentials!
|
||||||
|
# Use environment variables instead
|
||||||
|
api_key: "${MY_EXT_API_KEY}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config Loading
|
||||||
|
|
||||||
|
In your command, load config with layered precedence:
|
||||||
|
|
||||||
|
1. Extension defaults (`extension.yml` → `defaults`)
|
||||||
|
2. Project config (`.specify/extensions/my-ext/my-ext-config.yml`)
|
||||||
|
3. Local overrides (`.specify/extensions/my-ext/my-ext-config.local.yml` - gitignored)
|
||||||
|
4. Environment variables (`SPECKIT_MY_EXT_*`)
|
||||||
|
|
||||||
|
**Example loading script**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
EXT_DIR=".specify/extensions/my-ext"
|
||||||
|
|
||||||
|
# Load and merge config
|
||||||
|
config=$(yq eval '.' "$EXT_DIR/my-ext-config.yml" -o=json)
|
||||||
|
|
||||||
|
# Apply env overrides
|
||||||
|
if [ -n "${SPECKIT_MY_EXT_API_KEY:-}" ]; then
|
||||||
|
config=$(echo "$config" | jq ".api.api_key = \"$SPECKIT_MY_EXT_API_KEY\"")
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$config"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Excluding Files with `.extensionignore`
|
||||||
|
|
||||||
|
Extension authors can create a `.extensionignore` file in the extension root to exclude files and folders from being copied when a user installs the extension with `specify extension add`. This is useful for keeping development-only files (tests, CI configs, docs source, etc.) out of the installed copy.
|
||||||
|
|
||||||
|
### Format
|
||||||
|
|
||||||
|
The file uses `.gitignore`-compatible patterns (one per line), powered by the [`pathspec`](https://pypi.org/project/pathspec/) library:
|
||||||
|
|
||||||
|
- Blank lines are ignored
|
||||||
|
- Lines starting with `#` are comments
|
||||||
|
- `*` matches anything **except** `/` (does not cross directory boundaries)
|
||||||
|
- `**` matches zero or more directories (e.g., `docs/**/*.draft.md`)
|
||||||
|
- `?` matches any single character except `/`
|
||||||
|
- A trailing `/` restricts a pattern to directories only
|
||||||
|
- Patterns containing `/` (other than a trailing slash) are anchored to the extension root
|
||||||
|
- Patterns without `/` match at any depth in the tree
|
||||||
|
- `!` negates a previously excluded pattern (re-includes a file)
|
||||||
|
- Backslashes in patterns are normalised to forward slashes for cross-platform compatibility
|
||||||
|
- The `.extensionignore` file itself is always excluded automatically
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```gitignore
|
||||||
|
# .extensionignore
|
||||||
|
|
||||||
|
# Development files
|
||||||
|
tests/
|
||||||
|
.github/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Documentation source (keep only the built README)
|
||||||
|
docs/
|
||||||
|
CONTRIBUTING.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern Matching
|
||||||
|
|
||||||
|
| Pattern | Matches | Does NOT match |
|
||||||
|
|---------|---------|----------------|
|
||||||
|
| `*.pyc` | Any `.pyc` file in any directory | — |
|
||||||
|
| `tests/` | The `tests` directory (and all its contents) | A file named `tests` |
|
||||||
|
| `docs/*.draft.md` | `docs/api.draft.md` (directly inside `docs/`) | `docs/sub/api.draft.md` (nested) |
|
||||||
|
| `.env` | The `.env` file at any level | — |
|
||||||
|
| `!README.md` | Re-includes `README.md` even if matched by an earlier pattern | — |
|
||||||
|
| `docs/**/*.draft.md` | `docs/api.draft.md`, `docs/sub/api.draft.md` | — |
|
||||||
|
|
||||||
|
### Unsupported Features
|
||||||
|
|
||||||
|
The following `.gitignore` features are **not applicable** in this context:
|
||||||
|
|
||||||
|
- **Multiple `.extensionignore` files**: Only a single file at the extension root is supported (`.gitignore` supports files in subdirectories)
|
||||||
|
- **`$GIT_DIR/info/exclude` and `core.excludesFile`**: These are Git-specific and have no equivalent here
|
||||||
|
- **Negation inside excluded directories**: Because file copying uses `shutil.copytree`, excluding a directory prevents recursion into it entirely. A negation pattern cannot re-include a file inside a directory that was itself excluded. For example, the combination `tests/` followed by `!tests/important.py` will **not** preserve `tests/important.py` — the `tests/` directory is skipped at the root level and its contents are never evaluated. To work around this, exclude the directory's contents individually instead of the directory itself (e.g., `tests/*.pyc` and `tests/.cache/` rather than `tests/`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation Rules
|
||||||
|
|
||||||
|
### Extension ID
|
||||||
|
|
||||||
|
- **Pattern**: `^[a-z0-9-]+$`
|
||||||
|
- **Valid**: `my-ext`, `tool-123`, `awesome-plugin`
|
||||||
|
- **Invalid**: `MyExt` (uppercase), `my_ext` (underscore), `my ext` (space)
|
||||||
|
|
||||||
|
### Extension Version
|
||||||
|
|
||||||
|
- **Format**: Semantic versioning (MAJOR.MINOR.PATCH)
|
||||||
|
- **Valid**: `1.0.0`, `0.1.0`, `2.5.3`
|
||||||
|
- **Invalid**: `1.0`, `v1.0.0`, `1.0.0-beta`
|
||||||
|
|
||||||
|
### Command Name
|
||||||
|
|
||||||
|
- **Pattern**: `^speckit\.[a-z0-9-]+\.[a-z0-9-]+$`
|
||||||
|
- **Valid**: `speckit.my-ext.hello`, `speckit.tool.cmd`
|
||||||
|
- **Invalid**: `my-ext.hello` (missing prefix), `speckit.hello` (no extension namespace)
|
||||||
|
|
||||||
|
### Command File Path
|
||||||
|
|
||||||
|
- **Must be** relative to extension root
|
||||||
|
- **Valid**: `commands/hello.md`, `commands/subdir/cmd.md`
|
||||||
|
- **Invalid**: `/absolute/path.md`, `../outside.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Extensions
|
||||||
|
|
||||||
|
### Manual Testing
|
||||||
|
|
||||||
|
1. **Create test extension**
|
||||||
|
2. **Install locally**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add --dev /path/to/extension
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Verify installation**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension list
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test commands** with your AI agent
|
||||||
|
5. **Check command registration**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls .claude/commands/speckit.my-ext.*
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Remove extension**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension remove my-ext
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Testing
|
||||||
|
|
||||||
|
Create tests for your extension:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# tests/test_my_extension.py
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from specify_cli.extensions import ExtensionManifest
|
||||||
|
|
||||||
|
def test_manifest_valid():
|
||||||
|
"""Test extension manifest is valid."""
|
||||||
|
manifest = ExtensionManifest(Path("extension.yml"))
|
||||||
|
assert manifest.id == "my-ext"
|
||||||
|
assert len(manifest.commands) >= 1
|
||||||
|
|
||||||
|
def test_command_files_exist():
|
||||||
|
"""Test all command files exist."""
|
||||||
|
manifest = ExtensionManifest(Path("extension.yml"))
|
||||||
|
for cmd in manifest.commands:
|
||||||
|
cmd_file = Path(cmd["file"])
|
||||||
|
assert cmd_file.exists(), f"Command file not found: {cmd_file}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Distribution
|
||||||
|
|
||||||
|
### Option 1: GitHub Repository
|
||||||
|
|
||||||
|
1. **Create repository**: `spec-kit-my-ext`
|
||||||
|
2. **Add files**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
spec-kit-my-ext/
|
||||||
|
├── extension.yml
|
||||||
|
├── commands/
|
||||||
|
├── scripts/
|
||||||
|
├── docs/
|
||||||
|
├── README.md
|
||||||
|
├── LICENSE
|
||||||
|
└── CHANGELOG.md
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create release**: Tag with version (e.g., `v1.0.0`)
|
||||||
|
4. **Install from repo**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/you/spec-kit-my-ext
|
||||||
|
specify extension add --dev spec-kit-my-ext/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: ZIP Archive (Future)
|
||||||
|
|
||||||
|
Create ZIP archive and host on GitHub Releases:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zip -r spec-kit-my-ext-1.0.0.zip extension.yml commands/ scripts/ docs/
|
||||||
|
```
|
||||||
|
|
||||||
|
Users install with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add --from https://github.com/.../spec-kit-my-ext-1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Community Reference Catalog
|
||||||
|
|
||||||
|
Submit to the community catalog for public discovery:
|
||||||
|
|
||||||
|
1. **Fork** spec-kit repository
|
||||||
|
2. **Add entry** to `extensions/catalog.community.json`
|
||||||
|
3. **Update** `extensions/README.md` with your extension
|
||||||
|
4. **Create PR** following the [Extension Publishing Guide](EXTENSION-PUBLISHING-GUIDE.md)
|
||||||
|
5. **After merge**, your extension becomes available:
|
||||||
|
- Users can browse `catalog.community.json` to discover your extension
|
||||||
|
- Users copy the entry to their own `catalog.json`
|
||||||
|
- Users install with: `specify extension add my-ext` (from their catalog)
|
||||||
|
|
||||||
|
See the [Extension Publishing Guide](EXTENSION-PUBLISHING-GUIDE.md) for detailed submission instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
- **Extension ID**: Use descriptive, hyphenated names (`jira-integration`, not `ji`)
|
||||||
|
- **Commands**: Use verb-noun pattern (`create-issue`, `sync-status`)
|
||||||
|
- **Config files**: Match extension ID (`jira-config.yml`)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- **README.md**: Overview, installation, usage
|
||||||
|
- **CHANGELOG.md**: Version history
|
||||||
|
- **docs/**: Detailed guides
|
||||||
|
- **Command descriptions**: Clear, concise
|
||||||
|
|
||||||
|
### Versioning
|
||||||
|
|
||||||
|
- **Follow SemVer**: `MAJOR.MINOR.PATCH`
|
||||||
|
- **MAJOR**: Breaking changes
|
||||||
|
- **MINOR**: New features
|
||||||
|
- **PATCH**: Bug fixes
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- **Never commit secrets**: Use environment variables
|
||||||
|
- **Validate input**: Sanitize user arguments
|
||||||
|
- **Document permissions**: What files/APIs are accessed
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
|
||||||
|
- **Specify version range**: Don't require exact version
|
||||||
|
- **Test with multiple versions**: Ensure compatibility
|
||||||
|
- **Graceful degradation**: Handle missing features
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Extensions
|
||||||
|
|
||||||
|
### Minimal Extension
|
||||||
|
|
||||||
|
Smallest possible extension:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# extension.yml
|
||||||
|
schema_version: "1.0"
|
||||||
|
extension:
|
||||||
|
id: "minimal"
|
||||||
|
name: "Minimal Extension"
|
||||||
|
version: "1.0.0"
|
||||||
|
description: "Minimal example"
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.1.0"
|
||||||
|
provides:
|
||||||
|
commands:
|
||||||
|
- name: "speckit.minimal.hello"
|
||||||
|
file: "commands/hello.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
<!-- commands/hello.md -->
|
||||||
|
---
|
||||||
|
description: "Hello command"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hello World
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "Hello, $ARGUMENTS!"
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### Extension with Config
|
||||||
|
|
||||||
|
Extension using configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# extension.yml
|
||||||
|
# ... metadata ...
|
||||||
|
provides:
|
||||||
|
config:
|
||||||
|
- name: "tool-config.yml"
|
||||||
|
template: "tool-config.template.yml"
|
||||||
|
required: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# tool-config.template.yml
|
||||||
|
api_endpoint: "https://api.example.com"
|
||||||
|
timeout: 30
|
||||||
|
```
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
<!-- commands/use-config.md -->
|
||||||
|
# Use Config
|
||||||
|
|
||||||
|
Load config:
|
||||||
|
```bash
|
||||||
|
config_file=".specify/extensions/tool/tool-config.yml"
|
||||||
|
endpoint=$(yq eval '.api_endpoint' "$config_file")
|
||||||
|
echo "Using endpoint: $endpoint"
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### Extension with Hooks
|
||||||
|
|
||||||
|
Extension that runs automatically:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# extension.yml
|
||||||
|
hooks:
|
||||||
|
after_tasks:
|
||||||
|
command: "speckit.auto.analyze"
|
||||||
|
optional: false # Always run
|
||||||
|
description: "Analyze tasks after generation"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Extension won't install
|
||||||
|
|
||||||
|
**Error**: `Invalid extension ID`
|
||||||
|
|
||||||
|
- **Fix**: Use lowercase, alphanumeric + hyphens only
|
||||||
|
|
||||||
|
**Error**: `Extension requires spec-kit >=0.2.0`
|
||||||
|
|
||||||
|
- **Fix**: Update spec-kit with `uv tool install specify-cli --force`
|
||||||
|
|
||||||
|
**Error**: `Command file not found`
|
||||||
|
|
||||||
|
- **Fix**: Ensure command files exist at paths specified in manifest
|
||||||
|
|
||||||
|
### Commands not registered
|
||||||
|
|
||||||
|
**Symptom**: Commands don't appear in AI agent
|
||||||
|
|
||||||
|
**Check**:
|
||||||
|
|
||||||
|
1. `.claude/commands/` directory exists
|
||||||
|
2. Extension installed successfully
|
||||||
|
3. Commands registered in registry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat .specify/extensions/.registry
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix**: Reinstall extension to trigger registration
|
||||||
|
|
||||||
|
### Config not loading
|
||||||
|
|
||||||
|
**Check**:
|
||||||
|
|
||||||
|
1. Config file exists: `.specify/extensions/{ext-id}/{ext-id}-config.yml`
|
||||||
|
2. YAML syntax is valid: `yq eval '.' config.yml`
|
||||||
|
3. Environment variables set correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- **Issues**: Report bugs at GitHub repository
|
||||||
|
- **Discussions**: Ask questions in GitHub Discussions
|
||||||
|
- **Examples**: See `spec-kit-jira` for full-featured example (Phase B)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Create your extension** following this guide
|
||||||
|
2. **Test locally** with `--dev` flag
|
||||||
|
3. **Share with community** (GitHub, catalog)
|
||||||
|
4. **Iterate** based on feedback
|
||||||
|
|
||||||
|
Happy extending! 🚀
|
||||||
548
extensions/EXTENSION-PUBLISHING-GUIDE.md
Normal file
548
extensions/EXTENSION-PUBLISHING-GUIDE.md
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
# Extension Publishing Guide
|
||||||
|
|
||||||
|
This guide explains how to publish your extension to the Spec Kit extension catalog, making it discoverable by `specify extension search`.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Prerequisites](#prerequisites)
|
||||||
|
2. [Prepare Your Extension](#prepare-your-extension)
|
||||||
|
3. [Submit to Catalog](#submit-to-catalog)
|
||||||
|
4. [Verification Process](#verification-process)
|
||||||
|
5. [Release Workflow](#release-workflow)
|
||||||
|
6. [Best Practices](#best-practices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before publishing an extension, ensure you have:
|
||||||
|
|
||||||
|
1. **Valid Extension**: A working extension with a valid `extension.yml` manifest
|
||||||
|
2. **Git Repository**: Extension hosted on GitHub (or other public git hosting)
|
||||||
|
3. **Documentation**: README.md with installation and usage instructions
|
||||||
|
4. **License**: Open source license file (MIT, Apache 2.0, etc.)
|
||||||
|
5. **Versioning**: Semantic versioning (e.g., 1.0.0)
|
||||||
|
6. **Testing**: Extension tested on real projects
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prepare Your Extension
|
||||||
|
|
||||||
|
### 1. Extension Structure
|
||||||
|
|
||||||
|
Ensure your extension follows the standard structure:
|
||||||
|
|
||||||
|
```text
|
||||||
|
your-extension/
|
||||||
|
├── extension.yml # Required: Extension manifest
|
||||||
|
├── README.md # Required: Documentation
|
||||||
|
├── LICENSE # Required: License file
|
||||||
|
├── CHANGELOG.md # Recommended: Version history
|
||||||
|
├── .gitignore # Recommended: Git ignore rules
|
||||||
|
│
|
||||||
|
├── commands/ # Extension commands
|
||||||
|
│ ├── command1.md
|
||||||
|
│ └── command2.md
|
||||||
|
│
|
||||||
|
├── config-template.yml # Config template (if needed)
|
||||||
|
│
|
||||||
|
└── docs/ # Additional documentation
|
||||||
|
├── usage.md
|
||||||
|
└── examples/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. extension.yml Validation
|
||||||
|
|
||||||
|
Verify your manifest is valid:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
extension:
|
||||||
|
id: "your-extension" # Unique lowercase-hyphenated ID
|
||||||
|
name: "Your Extension Name" # Human-readable name
|
||||||
|
version: "1.0.0" # Semantic version
|
||||||
|
description: "Brief description (one sentence)"
|
||||||
|
author: "Your Name or Organization"
|
||||||
|
repository: "https://github.com/your-org/spec-kit-your-extension"
|
||||||
|
license: "MIT"
|
||||||
|
homepage: "https://github.com/your-org/spec-kit-your-extension"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.1.0" # Required spec-kit version
|
||||||
|
|
||||||
|
provides:
|
||||||
|
commands: # List all commands
|
||||||
|
- name: "speckit.your-extension.command"
|
||||||
|
file: "commands/command.md"
|
||||||
|
description: "Command description"
|
||||||
|
|
||||||
|
tags: # 2-5 relevant tags
|
||||||
|
- "category"
|
||||||
|
- "tool-name"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation Checklist**:
|
||||||
|
|
||||||
|
- ✅ `id` is lowercase with hyphens only (no underscores, spaces, or special characters)
|
||||||
|
- ✅ `version` follows semantic versioning (X.Y.Z)
|
||||||
|
- ✅ `description` is concise (under 100 characters)
|
||||||
|
- ✅ `repository` URL is valid and public
|
||||||
|
- ✅ All command files exist in the extension directory
|
||||||
|
- ✅ Tags are lowercase and descriptive
|
||||||
|
|
||||||
|
### 3. Create GitHub Release
|
||||||
|
|
||||||
|
Create a GitHub release for your extension version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tag the release
|
||||||
|
git tag v1.0.0
|
||||||
|
git push origin v1.0.0
|
||||||
|
|
||||||
|
# Create release on GitHub
|
||||||
|
# Go to: https://github.com/your-org/spec-kit-your-extension/releases/new
|
||||||
|
# - Tag: v1.0.0
|
||||||
|
# - Title: v1.0.0 - Release Name
|
||||||
|
# - Description: Changelog/release notes
|
||||||
|
```
|
||||||
|
|
||||||
|
The release archive URL will be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Test Installation
|
||||||
|
|
||||||
|
Test that users can install from your release:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test dev installation
|
||||||
|
specify extension add --dev /path/to/your-extension
|
||||||
|
|
||||||
|
# Test from GitHub archive
|
||||||
|
specify extension add --from https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Submit to Catalog
|
||||||
|
|
||||||
|
### Understanding the Catalogs
|
||||||
|
|
||||||
|
Spec Kit uses a dual-catalog system. For details about how catalogs work, see the main [Extensions README](README.md#extension-catalogs).
|
||||||
|
|
||||||
|
**For extension publishing**: All community extensions should be added to `catalog.community.json`. Users browse this catalog and copy extensions they trust into their own `catalog.json`.
|
||||||
|
|
||||||
|
### 1. Fork the spec-kit Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fork on GitHub
|
||||||
|
# https://github.com/github/spec-kit/fork
|
||||||
|
|
||||||
|
# Clone your fork
|
||||||
|
git clone https://github.com/YOUR-USERNAME/spec-kit.git
|
||||||
|
cd spec-kit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add Extension to Community Catalog
|
||||||
|
|
||||||
|
Edit `extensions/catalog.community.json` and add your extension:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-01-28T15:54:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
||||||
|
"extensions": {
|
||||||
|
"your-extension": {
|
||||||
|
"name": "Your Extension Name",
|
||||||
|
"id": "your-extension",
|
||||||
|
"description": "Brief description of your extension",
|
||||||
|
"author": "Your Name",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/your-org/spec-kit-your-extension",
|
||||||
|
"homepage": "https://github.com/your-org/spec-kit-your-extension",
|
||||||
|
"documentation": "https://github.com/your-org/spec-kit-your-extension/blob/main/docs/",
|
||||||
|
"changelog": "https://github.com/your-org/spec-kit-your-extension/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "required-mcp-tool",
|
||||||
|
"version": ">=1.0.0",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 3,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"category",
|
||||||
|
"tool-name",
|
||||||
|
"feature"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-01-28T00:00:00Z",
|
||||||
|
"updated_at": "2026-01-28T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**:
|
||||||
|
|
||||||
|
- Set `verified: false` (maintainers will verify)
|
||||||
|
- Set `downloads: 0` and `stars: 0` (auto-updated later)
|
||||||
|
- Use current timestamp for `created_at` and `updated_at`
|
||||||
|
- Update the top-level `updated_at` to current time
|
||||||
|
|
||||||
|
### 3. Update Extensions README
|
||||||
|
|
||||||
|
Add your extension to the Available Extensions table in `extensions/README.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
| Your Extension Name | Brief description of what it does | [repo-name](https://github.com/your-org/spec-kit-your-extension) |
|
||||||
|
```
|
||||||
|
|
||||||
|
Insert your extension in alphabetical order in the table.
|
||||||
|
|
||||||
|
### 4. Submit Pull Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a branch
|
||||||
|
git checkout -b add-your-extension
|
||||||
|
|
||||||
|
# Commit your changes
|
||||||
|
git add extensions/catalog.community.json extensions/README.md
|
||||||
|
git commit -m "Add your-extension to community catalog
|
||||||
|
|
||||||
|
- Extension ID: your-extension
|
||||||
|
- Version: 1.0.0
|
||||||
|
- Author: Your Name
|
||||||
|
- Description: Brief description
|
||||||
|
"
|
||||||
|
|
||||||
|
# Push to your fork
|
||||||
|
git push origin add-your-extension
|
||||||
|
|
||||||
|
# Create Pull Request on GitHub
|
||||||
|
# https://github.com/github/spec-kit/compare
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pull Request Template**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Extension Submission
|
||||||
|
|
||||||
|
**Extension Name**: Your Extension Name
|
||||||
|
**Extension ID**: your-extension
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Author**: Your Name
|
||||||
|
**Repository**: https://github.com/your-org/spec-kit-your-extension
|
||||||
|
|
||||||
|
### Description
|
||||||
|
Brief description of what your extension does.
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
- [x] Valid extension.yml manifest
|
||||||
|
- [x] README.md with installation and usage docs
|
||||||
|
- [x] LICENSE file included
|
||||||
|
- [x] GitHub release created (v1.0.0)
|
||||||
|
- [x] Extension tested on real project
|
||||||
|
- [x] All commands working
|
||||||
|
- [x] No security vulnerabilities
|
||||||
|
- [x] Added to extensions/catalog.community.json
|
||||||
|
- [x] Added to extensions/README.md Available Extensions table
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
Tested on:
|
||||||
|
- macOS 13.0+ with spec-kit 0.1.0
|
||||||
|
- Project: [Your test project]
|
||||||
|
|
||||||
|
### Additional Notes
|
||||||
|
Any additional context or notes for reviewers.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Process
|
||||||
|
|
||||||
|
### What Happens After Submission
|
||||||
|
|
||||||
|
1. **Automated Checks** (if available):
|
||||||
|
- Manifest validation
|
||||||
|
- Download URL accessibility
|
||||||
|
- Repository existence
|
||||||
|
- License file presence
|
||||||
|
|
||||||
|
2. **Manual Review**:
|
||||||
|
- Code quality review
|
||||||
|
- Security audit
|
||||||
|
- Functionality testing
|
||||||
|
- Documentation review
|
||||||
|
|
||||||
|
3. **Verification**:
|
||||||
|
- If approved, `verified: true` is set
|
||||||
|
- Extension appears in `specify extension search --verified`
|
||||||
|
|
||||||
|
### Verification Criteria
|
||||||
|
|
||||||
|
To be verified, your extension must:
|
||||||
|
|
||||||
|
✅ **Functionality**:
|
||||||
|
|
||||||
|
- Works as described in documentation
|
||||||
|
- All commands execute without errors
|
||||||
|
- No breaking changes to user workflows
|
||||||
|
|
||||||
|
✅ **Security**:
|
||||||
|
|
||||||
|
- No known vulnerabilities
|
||||||
|
- No malicious code
|
||||||
|
- Safe handling of user data
|
||||||
|
- Proper validation of inputs
|
||||||
|
|
||||||
|
✅ **Code Quality**:
|
||||||
|
|
||||||
|
- Clean, readable code
|
||||||
|
- Follows extension best practices
|
||||||
|
- Proper error handling
|
||||||
|
- Helpful error messages
|
||||||
|
|
||||||
|
✅ **Documentation**:
|
||||||
|
|
||||||
|
- Clear installation instructions
|
||||||
|
- Usage examples
|
||||||
|
- Troubleshooting section
|
||||||
|
- Accurate description
|
||||||
|
|
||||||
|
✅ **Maintenance**:
|
||||||
|
|
||||||
|
- Active repository
|
||||||
|
- Responsive to issues
|
||||||
|
- Regular updates
|
||||||
|
- Semantic versioning followed
|
||||||
|
|
||||||
|
### Typical Review Timeline
|
||||||
|
|
||||||
|
- **Automated checks**: Immediate (if implemented)
|
||||||
|
- **Manual review**: 3-7 business days
|
||||||
|
- **Verification**: After successful review
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Release Workflow
|
||||||
|
|
||||||
|
### Publishing New Versions
|
||||||
|
|
||||||
|
When releasing a new version:
|
||||||
|
|
||||||
|
1. **Update version** in `extension.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extension:
|
||||||
|
version: "1.1.0" # Updated version
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update CHANGELOG.md**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## [1.1.0] - 2026-02-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- New feature X
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bug fix Y
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create GitHub release**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag v1.1.0
|
||||||
|
git push origin v1.1.0
|
||||||
|
# Create release on GitHub
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update catalog**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fork spec-kit repo (or update existing fork)
|
||||||
|
cd spec-kit
|
||||||
|
|
||||||
|
# Update extensions/catalog.json
|
||||||
|
jq '.extensions["your-extension"].version = "1.1.0"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
|
||||||
|
jq '.extensions["your-extension"].download_url = "https://github.com/your-org/spec-kit-your-extension/archive/refs/tags/v1.1.0.zip"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
|
||||||
|
jq '.extensions["your-extension"].updated_at = "2026-02-15T00:00:00Z"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
|
||||||
|
jq '.updated_at = "2026-02-15T00:00:00Z"' extensions/catalog.json > tmp.json && mv tmp.json extensions/catalog.json
|
||||||
|
|
||||||
|
# Submit PR
|
||||||
|
git checkout -b update-your-extension-v1.1.0
|
||||||
|
git add extensions/catalog.json
|
||||||
|
git commit -m "Update your-extension to v1.1.0"
|
||||||
|
git push origin update-your-extension-v1.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Submit update PR** with changelog in description
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Extension Design
|
||||||
|
|
||||||
|
1. **Single Responsibility**: Each extension should focus on one tool/integration
|
||||||
|
2. **Clear Naming**: Use descriptive, unambiguous names
|
||||||
|
3. **Minimal Dependencies**: Avoid unnecessary dependencies
|
||||||
|
4. **Backward Compatibility**: Follow semantic versioning strictly
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
1. **README.md Structure**:
|
||||||
|
- Overview and features
|
||||||
|
- Installation instructions
|
||||||
|
- Configuration guide
|
||||||
|
- Usage examples
|
||||||
|
- Troubleshooting
|
||||||
|
- Contributing guidelines
|
||||||
|
|
||||||
|
2. **Command Documentation**:
|
||||||
|
- Clear description
|
||||||
|
- Prerequisites listed
|
||||||
|
- Step-by-step instructions
|
||||||
|
- Error handling guidance
|
||||||
|
- Examples
|
||||||
|
|
||||||
|
3. **Configuration**:
|
||||||
|
- Provide template file
|
||||||
|
- Document all options
|
||||||
|
- Include examples
|
||||||
|
- Explain defaults
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
1. **Input Validation**: Validate all user inputs
|
||||||
|
2. **No Hardcoded Secrets**: Never include credentials
|
||||||
|
3. **Safe Dependencies**: Only use trusted dependencies
|
||||||
|
4. **Audit Regularly**: Check for vulnerabilities
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
1. **Respond to Issues**: Address issues within 1-2 weeks
|
||||||
|
2. **Regular Updates**: Keep dependencies updated
|
||||||
|
3. **Changelog**: Maintain detailed changelog
|
||||||
|
4. **Deprecation**: Give advance notice for breaking changes
|
||||||
|
|
||||||
|
### Community
|
||||||
|
|
||||||
|
1. **License**: Use permissive open-source license (MIT, Apache 2.0)
|
||||||
|
2. **Contributing**: Welcome contributions
|
||||||
|
3. **Code of Conduct**: Be respectful and inclusive
|
||||||
|
4. **Support**: Provide ways to get help (issues, discussions, email)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Q: Can I publish private/proprietary extensions?
|
||||||
|
|
||||||
|
A: The main catalog is for public extensions only. For private extensions:
|
||||||
|
|
||||||
|
- Host your own catalog.json file
|
||||||
|
- Users add your catalog: `specify extension add-catalog https://your-domain.com/catalog.json`
|
||||||
|
- Not yet implemented - coming in Phase 4
|
||||||
|
|
||||||
|
### Q: How long does verification take?
|
||||||
|
|
||||||
|
A: Typically 3-7 business days for initial review. Updates to verified extensions are usually faster.
|
||||||
|
|
||||||
|
### Q: What if my extension is rejected?
|
||||||
|
|
||||||
|
A: You'll receive feedback on what needs to be fixed. Make the changes and resubmit.
|
||||||
|
|
||||||
|
### Q: Can I update my extension anytime?
|
||||||
|
|
||||||
|
A: Yes, submit a PR to update the catalog with your new version. Verified status may be re-evaluated for major changes.
|
||||||
|
|
||||||
|
### Q: Do I need to be verified to be in the catalog?
|
||||||
|
|
||||||
|
A: No, unverified extensions are still searchable. Verification just adds trust and visibility.
|
||||||
|
|
||||||
|
### Q: Can extensions have paid features?
|
||||||
|
|
||||||
|
A: Extensions should be free and open-source. Commercial support/services are allowed, but core functionality must be free.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- **Catalog Issues**: <https://github.com/statsperform/spec-kit/issues>
|
||||||
|
- **Extension Template**: <https://github.com/statsperform/spec-kit-extension-template> (coming soon)
|
||||||
|
- **Development Guide**: See EXTENSION-DEVELOPMENT-GUIDE.md
|
||||||
|
- **Community**: Discussions and Q&A
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix: Catalog Schema
|
||||||
|
|
||||||
|
### Complete Catalog Entry Schema
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "string (required)",
|
||||||
|
"id": "string (required, unique)",
|
||||||
|
"description": "string (required, <200 chars)",
|
||||||
|
"author": "string (required)",
|
||||||
|
"version": "string (required, semver)",
|
||||||
|
"download_url": "string (required, valid URL)",
|
||||||
|
"repository": "string (required, valid URL)",
|
||||||
|
"homepage": "string (optional, valid URL)",
|
||||||
|
"documentation": "string (optional, valid URL)",
|
||||||
|
"changelog": "string (optional, valid URL)",
|
||||||
|
"license": "string (required)",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": "string (required, version specifier)",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "string (required)",
|
||||||
|
"version": "string (optional, version specifier)",
|
||||||
|
"required": "boolean (default: false)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": "integer (optional)",
|
||||||
|
"hooks": "integer (optional)"
|
||||||
|
},
|
||||||
|
"tags": ["array of strings (2-10 tags)"],
|
||||||
|
"verified": "boolean (default: false)",
|
||||||
|
"downloads": "integer (auto-updated)",
|
||||||
|
"stars": "integer (auto-updated)",
|
||||||
|
"created_at": "string (ISO 8601 datetime)",
|
||||||
|
"updated_at": "string (ISO 8601 datetime)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Valid Tags
|
||||||
|
|
||||||
|
Recommended tag categories:
|
||||||
|
|
||||||
|
- **Integration**: jira, linear, github, gitlab, azure-devops
|
||||||
|
- **Category**: issue-tracking, vcs, ci-cd, documentation, testing
|
||||||
|
- **Platform**: atlassian, microsoft, google
|
||||||
|
- **Feature**: automation, reporting, deployment, monitoring
|
||||||
|
|
||||||
|
Use 2-5 tags that best describe your extension.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last Updated: 2026-01-28*
|
||||||
|
*Catalog Format Version: 1.0*
|
||||||
990
extensions/EXTENSION-USER-GUIDE.md
Normal file
990
extensions/EXTENSION-USER-GUIDE.md
Normal file
@@ -0,0 +1,990 @@
|
|||||||
|
# Extension User Guide
|
||||||
|
|
||||||
|
Complete guide for using Spec Kit extensions to enhance your workflow.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Introduction](#introduction)
|
||||||
|
2. [Getting Started](#getting-started)
|
||||||
|
3. [Finding Extensions](#finding-extensions)
|
||||||
|
4. [Installing Extensions](#installing-extensions)
|
||||||
|
5. [Using Extensions](#using-extensions)
|
||||||
|
6. [Managing Extensions](#managing-extensions)
|
||||||
|
7. [Configuration](#configuration)
|
||||||
|
8. [Troubleshooting](#troubleshooting)
|
||||||
|
9. [Best Practices](#best-practices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
### What are Extensions?
|
||||||
|
|
||||||
|
Extensions are modular packages that add new commands and functionality to Spec Kit without bloating the core framework. They allow you to:
|
||||||
|
|
||||||
|
- **Integrate** with external tools (Jira, Linear, GitHub, etc.)
|
||||||
|
- **Automate** repetitive tasks with hooks
|
||||||
|
- **Customize** workflows for your team
|
||||||
|
- **Share** solutions across projects
|
||||||
|
|
||||||
|
### Why Use Extensions?
|
||||||
|
|
||||||
|
- **Clean Core**: Keeps spec-kit lightweight and focused
|
||||||
|
- **Optional Features**: Only install what you need
|
||||||
|
- **Community Driven**: Anyone can create and share extensions
|
||||||
|
- **Version Controlled**: Extensions are versioned independently
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Spec Kit version 0.1.0 or higher
|
||||||
|
- A spec-kit project (directory with `.specify/` folder)
|
||||||
|
|
||||||
|
### Check Your Version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify version
|
||||||
|
# Should show 0.1.0 or higher
|
||||||
|
```
|
||||||
|
|
||||||
|
### First Extension
|
||||||
|
|
||||||
|
Let's install the Jira extension as an example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Search for the extension
|
||||||
|
specify extension search jira
|
||||||
|
|
||||||
|
# 2. Get detailed information
|
||||||
|
specify extension info jira
|
||||||
|
|
||||||
|
# 3. Install it
|
||||||
|
specify extension add jira
|
||||||
|
|
||||||
|
# 4. Configure it
|
||||||
|
vim .specify/extensions/jira/jira-config.yml
|
||||||
|
|
||||||
|
# 5. Use it
|
||||||
|
# (Commands are now available in Claude Code)
|
||||||
|
/speckit.jira.specstoissues
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Finding Extensions
|
||||||
|
|
||||||
|
`specify extension search` searches **all active catalogs** simultaneously, including the community catalog by default. Results are annotated with their source catalog and install status.
|
||||||
|
|
||||||
|
### Browse All Extensions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension search
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows all extensions across all active catalogs (default and community by default).
|
||||||
|
|
||||||
|
### Search by Keyword
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search for "jira"
|
||||||
|
specify extension search jira
|
||||||
|
|
||||||
|
# Search for "issue tracking"
|
||||||
|
specify extension search issue
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filter by Tag
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all issue-tracking extensions
|
||||||
|
specify extension search --tag issue-tracking
|
||||||
|
|
||||||
|
# Find all Atlassian tools
|
||||||
|
specify extension search --tag atlassian
|
||||||
|
```
|
||||||
|
|
||||||
|
### Filter by Author
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Extensions by Stats Perform
|
||||||
|
specify extension search --author "Stats Perform"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Show Verified Only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Only show verified extensions
|
||||||
|
specify extension search --verified
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Extension Details
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Detailed information
|
||||||
|
specify extension info jira
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows:
|
||||||
|
|
||||||
|
- Description
|
||||||
|
- Requirements
|
||||||
|
- Commands provided
|
||||||
|
- Hooks available
|
||||||
|
- Links (documentation, repository, changelog)
|
||||||
|
- Installation status
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installing Extensions
|
||||||
|
|
||||||
|
### Install from Catalog
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# By name (from catalog)
|
||||||
|
specify extension add jira
|
||||||
|
```
|
||||||
|
|
||||||
|
This will:
|
||||||
|
|
||||||
|
1. Download the extension from GitHub
|
||||||
|
2. Validate the manifest
|
||||||
|
3. Check compatibility with your spec-kit version
|
||||||
|
4. Install to `.specify/extensions/jira/`
|
||||||
|
5. Register commands with your AI agent
|
||||||
|
6. Create config template
|
||||||
|
|
||||||
|
### Install from URL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From GitHub release
|
||||||
|
specify extension add --from https://github.com/org/spec-kit-ext/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install from Local Directory (Development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For testing or development
|
||||||
|
specify extension add --dev /path/to/extension
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
✓ Extension installed successfully!
|
||||||
|
|
||||||
|
Jira Integration (v1.0.0)
|
||||||
|
Create Jira Epics, Stories, and Issues from spec-kit artifacts
|
||||||
|
|
||||||
|
Provided commands:
|
||||||
|
• speckit.jira.specstoissues - Create Jira hierarchy from spec and tasks
|
||||||
|
• speckit.jira.discover-fields - Discover Jira custom fields for configuration
|
||||||
|
• speckit.jira.sync-status - Sync task completion status to Jira
|
||||||
|
|
||||||
|
⚠ Configuration may be required
|
||||||
|
Check: .specify/extensions/jira/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Using Extensions
|
||||||
|
|
||||||
|
### Using Extension Commands
|
||||||
|
|
||||||
|
Extensions add commands that appear in your AI agent (Claude Code):
|
||||||
|
|
||||||
|
```text
|
||||||
|
# In Claude Code
|
||||||
|
> /speckit.jira.specstoissues
|
||||||
|
|
||||||
|
# Or use short alias (if provided)
|
||||||
|
> /speckit.specstoissues
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extension Configuration
|
||||||
|
|
||||||
|
Most extensions require configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Find the config file
|
||||||
|
ls .specify/extensions/jira/
|
||||||
|
|
||||||
|
# 2. Copy template to config
|
||||||
|
cp .specify/extensions/jira/jira-config.template.yml \
|
||||||
|
.specify/extensions/jira/jira-config.yml
|
||||||
|
|
||||||
|
# 3. Edit configuration
|
||||||
|
vim .specify/extensions/jira/jira-config.yml
|
||||||
|
|
||||||
|
# 4. Use the extension
|
||||||
|
# (Commands will now work with your config)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extension Hooks
|
||||||
|
|
||||||
|
Some extensions provide hooks that execute after core commands:
|
||||||
|
|
||||||
|
**Example**: Jira extension hooks into `/speckit.tasks`
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Run core command
|
||||||
|
> /speckit.tasks
|
||||||
|
|
||||||
|
# Output includes:
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Hook**: jira
|
||||||
|
Command: `/speckit.jira.specstoissues`
|
||||||
|
Description: Automatically create Jira hierarchy after task generation
|
||||||
|
|
||||||
|
Prompt: Create Jira issues from tasks?
|
||||||
|
To execute: `/speckit.jira.specstoissues`
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then choose to run the hook or skip it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Managing Extensions
|
||||||
|
|
||||||
|
### List Installed Extensions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension list
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Installed Extensions:
|
||||||
|
|
||||||
|
✓ Jira Integration (v1.0.0)
|
||||||
|
Create Jira Epics, Stories, and Issues from spec-kit artifacts
|
||||||
|
Commands: 3 | Hooks: 1 | Status: Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Extensions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check for updates (all extensions)
|
||||||
|
specify extension update
|
||||||
|
|
||||||
|
# Update specific extension
|
||||||
|
specify extension update jira
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
🔄 Checking for updates...
|
||||||
|
|
||||||
|
Updates available:
|
||||||
|
|
||||||
|
• jira: 1.0.0 → 1.1.0
|
||||||
|
|
||||||
|
Update these extensions? [y/N]:
|
||||||
|
```
|
||||||
|
|
||||||
|
### Disable Extension Temporarily
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Disable without removing
|
||||||
|
specify extension disable jira
|
||||||
|
|
||||||
|
✓ Extension 'jira' disabled
|
||||||
|
|
||||||
|
Commands will no longer be available. Hooks will not execute.
|
||||||
|
To re-enable: specify extension enable jira
|
||||||
|
```
|
||||||
|
|
||||||
|
### Re-enable Extension
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension enable jira
|
||||||
|
|
||||||
|
✓ Extension 'jira' enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remove Extension
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove extension (with confirmation)
|
||||||
|
specify extension remove jira
|
||||||
|
|
||||||
|
# Keep configuration when removing
|
||||||
|
specify extension remove jira --keep-config
|
||||||
|
|
||||||
|
# Force removal (no confirmation)
|
||||||
|
specify extension remove jira --force
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
|
||||||
|
Extensions can have multiple configuration files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
.specify/extensions/jira/
|
||||||
|
├── jira-config.yml # Main config (version controlled)
|
||||||
|
├── jira-config.local.yml # Local overrides (gitignored)
|
||||||
|
└── jira-config.template.yml # Template (reference)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration Layers
|
||||||
|
|
||||||
|
Configuration is merged in this order (highest priority last):
|
||||||
|
|
||||||
|
1. **Extension defaults** (from `extension.yml`)
|
||||||
|
2. **Project config** (`jira-config.yml`)
|
||||||
|
3. **Local overrides** (`jira-config.local.yml`)
|
||||||
|
4. **Environment variables** (`SPECKIT_JIRA_*`)
|
||||||
|
|
||||||
|
### Example: Jira Configuration
|
||||||
|
|
||||||
|
**Project config** (`.specify/extensions/jira/jira-config.yml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
project:
|
||||||
|
key: "MSATS"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
epic:
|
||||||
|
labels: ["spec-driven"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Local override** (`.specify/extensions/jira/jira-config.local.yml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
project:
|
||||||
|
key: "MYTEST" # Override for local development
|
||||||
|
```
|
||||||
|
|
||||||
|
**Environment variable**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export SPECKIT_JIRA_PROJECT_KEY="DEVTEST"
|
||||||
|
```
|
||||||
|
|
||||||
|
Final resolved config uses `DEVTEST` from environment variable.
|
||||||
|
|
||||||
|
### Project-Wide Extension Settings
|
||||||
|
|
||||||
|
File: `.specify/extensions.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Extensions installed in this project
|
||||||
|
installed:
|
||||||
|
- jira
|
||||||
|
- linear
|
||||||
|
|
||||||
|
# Global settings
|
||||||
|
settings:
|
||||||
|
auto_execute_hooks: true
|
||||||
|
|
||||||
|
# Hook configuration
|
||||||
|
hooks:
|
||||||
|
after_tasks:
|
||||||
|
- extension: jira
|
||||||
|
command: speckit.jira.specstoissues
|
||||||
|
enabled: true
|
||||||
|
optional: true
|
||||||
|
prompt: "Create Jira issues from tasks?"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core Environment Variables
|
||||||
|
|
||||||
|
In addition to extension-specific environment variables (`SPECKIT_{EXT_ID}_*`), spec-kit supports core environment variables:
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `SPECKIT_CATALOG_URL` | Override the full catalog stack with a single URL (backward compat) | Built-in default stack |
|
||||||
|
| `GH_TOKEN` / `GITHUB_TOKEN` | GitHub API token for downloads | None |
|
||||||
|
|
||||||
|
#### Example: Using a custom catalog for testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Point to a local or alternative catalog (replaces the full stack)
|
||||||
|
export SPECKIT_CATALOG_URL="http://localhost:8000/catalog.json"
|
||||||
|
|
||||||
|
# Or use a staging catalog
|
||||||
|
export SPECKIT_CATALOG_URL="https://example.com/staging/catalog.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Extension Catalogs
|
||||||
|
|
||||||
|
Spec Kit uses a **catalog stack** — an ordered list of catalogs searched simultaneously. By default, two catalogs are active:
|
||||||
|
|
||||||
|
| Priority | Catalog | Install Allowed | Purpose |
|
||||||
|
|----------|---------|-----------------|---------|
|
||||||
|
| 1 | `catalog.json` (default) | ✅ Yes | Curated extensions available for installation |
|
||||||
|
| 2 | `catalog.community.json` (community) | ❌ No (discovery only) | Browse community extensions |
|
||||||
|
|
||||||
|
### Listing Active Catalogs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension catalog list
|
||||||
|
```
|
||||||
|
|
||||||
|
### Managing Catalogs via CLI
|
||||||
|
|
||||||
|
You can view the main catalog management commands using `--help`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
specify extension catalog --help
|
||||||
|
|
||||||
|
Usage: specify extension catalog [OPTIONS] COMMAND [ARGS]...
|
||||||
|
|
||||||
|
Manage extension catalogs
|
||||||
|
╭─ Options ────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ --help Show this message and exit. │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
╭─ Commands ───────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ list List all active extension catalogs. │
|
||||||
|
│ add Add a catalog to .specify/extension-catalogs.yml. │
|
||||||
|
│ remove Remove a catalog from .specify/extension-catalogs.yml. │
|
||||||
|
╰──────────────────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a Catalog (Project-scoped)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add an internal catalog that allows installs
|
||||||
|
specify extension catalog add \
|
||||||
|
--name "internal" \
|
||||||
|
--priority 2 \
|
||||||
|
--install-allowed \
|
||||||
|
https://internal.company.com/spec-kit/catalog.json
|
||||||
|
|
||||||
|
# Add a discovery-only catalog
|
||||||
|
specify extension catalog add \
|
||||||
|
--name "partner" \
|
||||||
|
--priority 5 \
|
||||||
|
https://partner.example.com/spec-kit/catalog.json
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates or updates `.specify/extension-catalogs.yml`.
|
||||||
|
|
||||||
|
### Removing a Catalog
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension catalog remove internal
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Config File
|
||||||
|
|
||||||
|
You can also edit `.specify/extension-catalogs.yml` directly:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
catalogs:
|
||||||
|
- name: "default"
|
||||||
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json"
|
||||||
|
priority: 1
|
||||||
|
install_allowed: true
|
||||||
|
description: "Built-in catalog of installable extensions"
|
||||||
|
|
||||||
|
- name: "internal"
|
||||||
|
url: "https://internal.company.com/spec-kit/catalog.json"
|
||||||
|
priority: 2
|
||||||
|
install_allowed: true
|
||||||
|
description: "Internal company extensions"
|
||||||
|
|
||||||
|
- name: "community"
|
||||||
|
url: "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json"
|
||||||
|
priority: 3
|
||||||
|
install_allowed: false
|
||||||
|
description: "Community-contributed extensions (discovery only)"
|
||||||
|
```
|
||||||
|
|
||||||
|
A user-level equivalent lives at `~/.specify/extension-catalogs.yml`. Project-level config takes full precedence when it contains one or more catalog entries. An empty `catalogs: []` list falls back to built-in defaults.
|
||||||
|
|
||||||
|
## Organization Catalog Customization
|
||||||
|
|
||||||
|
### Why Customize Your Catalog
|
||||||
|
|
||||||
|
Organizations customize their catalogs to:
|
||||||
|
|
||||||
|
- **Control available extensions** - Curate which extensions your team can install
|
||||||
|
- **Host private extensions** - Internal tools that shouldn't be public
|
||||||
|
- **Customize for compliance** - Meet security/audit requirements
|
||||||
|
- **Support air-gapped environments** - Work without internet access
|
||||||
|
|
||||||
|
### Setting Up a Custom Catalog
|
||||||
|
|
||||||
|
#### 1. Create Your Catalog File
|
||||||
|
|
||||||
|
Create a `catalog.json` file with your extensions:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-02-03T00:00:00Z",
|
||||||
|
"catalog_url": "https://your-org.com/spec-kit/catalog.json",
|
||||||
|
"extensions": {
|
||||||
|
"jira": {
|
||||||
|
"name": "Jira Integration",
|
||||||
|
"id": "jira",
|
||||||
|
"description": "Create Jira issues from spec-kit artifacts",
|
||||||
|
"author": "Your Organization",
|
||||||
|
"version": "2.1.0",
|
||||||
|
"download_url": "https://github.com/your-org/spec-kit-jira/archive/refs/tags/v2.1.0.zip",
|
||||||
|
"repository": "https://github.com/your-org/spec-kit-jira",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0",
|
||||||
|
"tools": [
|
||||||
|
{"name": "atlassian-mcp-server", "required": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 3,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": ["jira", "atlassian", "issue-tracking"],
|
||||||
|
"verified": true
|
||||||
|
},
|
||||||
|
"internal-tool": {
|
||||||
|
"name": "Internal Tool Integration",
|
||||||
|
"id": "internal-tool",
|
||||||
|
"description": "Connect to internal company systems",
|
||||||
|
"author": "Your Organization",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://internal.your-org.com/extensions/internal-tool-1.0.0.zip",
|
||||||
|
"repository": "https://github.internal.your-org.com/spec-kit-internal",
|
||||||
|
"license": "Proprietary",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 2
|
||||||
|
},
|
||||||
|
"tags": ["internal", "proprietary"],
|
||||||
|
"verified": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Host the Catalog
|
||||||
|
|
||||||
|
Options for hosting your catalog:
|
||||||
|
|
||||||
|
| Method | URL Example | Use Case |
|
||||||
|
| ------ | ----------- | -------- |
|
||||||
|
| GitHub Pages | `https://your-org.github.io/spec-kit-catalog/catalog.json` | Public or org-visible |
|
||||||
|
| Internal web server | `https://internal.company.com/spec-kit/catalog.json` | Corporate network |
|
||||||
|
| S3/Cloud storage | `https://s3.amazonaws.com/your-bucket/catalog.json` | Cloud-hosted teams |
|
||||||
|
| Local file server | `http://localhost:8000/catalog.json` | Development/testing |
|
||||||
|
|
||||||
|
**Security requirement**: URLs must use HTTPS (except `localhost` for testing).
|
||||||
|
|
||||||
|
#### 3. Configure Your Environment
|
||||||
|
|
||||||
|
##### Option A: Catalog stack config file (recommended)
|
||||||
|
|
||||||
|
Add to `.specify/extension-catalogs.yml` in your project:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
catalogs:
|
||||||
|
- name: "my-org"
|
||||||
|
url: "https://your-org.com/spec-kit/catalog.json"
|
||||||
|
priority: 1
|
||||||
|
install_allowed: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use the CLI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension catalog add \
|
||||||
|
--name "my-org" \
|
||||||
|
--install-allowed \
|
||||||
|
https://your-org.com/spec-kit/catalog.json
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Option B: Environment variable (recommended for CI/CD, single-catalog)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In ~/.bashrc, ~/.zshrc, or CI pipeline
|
||||||
|
export SPECKIT_CATALOG_URL="https://your-org.com/spec-kit/catalog.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Verify Configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List active catalogs
|
||||||
|
specify extension catalog list
|
||||||
|
|
||||||
|
# Search should now show your catalog's extensions
|
||||||
|
specify extension search
|
||||||
|
|
||||||
|
# Install from your catalog
|
||||||
|
specify extension add jira
|
||||||
|
```
|
||||||
|
|
||||||
|
### Catalog JSON Schema
|
||||||
|
|
||||||
|
Required fields for each extension entry:
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
| ----- | ---- | -------- | ----------- |
|
||||||
|
| `name` | string | Yes | Human-readable name |
|
||||||
|
| `id` | string | Yes | Unique identifier (lowercase, hyphens) |
|
||||||
|
| `version` | string | Yes | Semantic version (X.Y.Z) |
|
||||||
|
| `download_url` | string | Yes | URL to ZIP archive |
|
||||||
|
| `repository` | string | Yes | Source code URL |
|
||||||
|
| `description` | string | No | Brief description |
|
||||||
|
| `author` | string | No | Author/organization |
|
||||||
|
| `license` | string | No | SPDX license identifier |
|
||||||
|
| `requires.speckit_version` | string | No | Version constraint |
|
||||||
|
| `requires.tools` | array | No | Required external tools |
|
||||||
|
| `provides.commands` | number | No | Number of commands |
|
||||||
|
| `provides.hooks` | number | No | Number of hooks |
|
||||||
|
| `tags` | array | No | Search tags |
|
||||||
|
| `verified` | boolean | No | Verification status |
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
#### Private/Internal Extensions
|
||||||
|
|
||||||
|
Host proprietary extensions that integrate with internal systems:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"internal-auth": {
|
||||||
|
"name": "Internal SSO Integration",
|
||||||
|
"download_url": "https://artifactory.company.com/spec-kit/internal-auth-1.0.0.zip",
|
||||||
|
"verified": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Curated Team Catalog
|
||||||
|
|
||||||
|
Limit which extensions your team can install:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"jira": { "..." },
|
||||||
|
"github": { "..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Only `jira` and `github` will appear in `specify extension search`.
|
||||||
|
|
||||||
|
#### Air-Gapped Environments
|
||||||
|
|
||||||
|
For networks without internet access:
|
||||||
|
|
||||||
|
1. Download extension ZIPs to internal file server
|
||||||
|
2. Create catalog pointing to internal URLs
|
||||||
|
3. Host catalog on internal web server
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jira": {
|
||||||
|
"download_url": "https://files.internal/spec-kit/jira-2.1.0.zip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Development/Testing
|
||||||
|
|
||||||
|
Test new extensions before publishing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start local server
|
||||||
|
python -m http.server 8000 --directory ./my-catalog/
|
||||||
|
|
||||||
|
# Point spec-kit to local catalog
|
||||||
|
export SPECKIT_CATALOG_URL="http://localhost:8000/catalog.json"
|
||||||
|
|
||||||
|
# Test installation
|
||||||
|
specify extension add my-new-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combining with Direct Installation
|
||||||
|
|
||||||
|
You can still install extensions not in your catalog using `--from`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From catalog
|
||||||
|
specify extension add jira
|
||||||
|
|
||||||
|
# Direct URL (bypasses catalog)
|
||||||
|
specify extension add --from https://github.com/someone/spec-kit-ext/archive/v1.0.0.zip
|
||||||
|
|
||||||
|
# Local development
|
||||||
|
specify extension add --dev /path/to/extension
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Direct URL installation shows a security warning since the extension isn't from your configured catalog.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Extension Not Found
|
||||||
|
|
||||||
|
**Error**: `Extension 'jira' not found in catalog
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check spelling: `specify extension search jira`
|
||||||
|
2. Refresh catalog: `specify extension search --help`
|
||||||
|
3. Check internet connection
|
||||||
|
4. Extension may not be published yet
|
||||||
|
|
||||||
|
### Configuration Not Found
|
||||||
|
|
||||||
|
**Error**: `Jira configuration not found`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check if extension is installed: `specify extension list`
|
||||||
|
2. Create config from template:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .specify/extensions/jira/jira-config.template.yml \
|
||||||
|
.specify/extensions/jira/jira-config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Reinstall extension: `specify extension remove jira && specify extension add jira`
|
||||||
|
|
||||||
|
### Command Not Available
|
||||||
|
|
||||||
|
**Issue**: Extension command not appearing in AI agent
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check extension is enabled: `specify extension list`
|
||||||
|
2. Restart AI agent (Claude Code)
|
||||||
|
3. Check command file exists:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls .claude/commands/speckit.jira.*.md
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Reinstall extension
|
||||||
|
|
||||||
|
### Incompatible Version
|
||||||
|
|
||||||
|
**Error**: `Extension requires spec-kit >=0.2.0, but you have 0.1.0`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Upgrade spec-kit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv tool upgrade specify-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install older version of extension:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add --from https://github.com/org/ext/archive/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### MCP Tool Not Available
|
||||||
|
|
||||||
|
**Error**: `Tool 'jira-mcp-server/epic_create' not found`
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check MCP server is installed
|
||||||
|
2. Check AI agent MCP configuration
|
||||||
|
3. Restart AI agent
|
||||||
|
4. Check extension requirements: `specify extension info jira`
|
||||||
|
|
||||||
|
### Permission Denied
|
||||||
|
|
||||||
|
**Error**: `Permission denied` when accessing Jira
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check Jira credentials in MCP server config
|
||||||
|
2. Verify project permissions in Jira
|
||||||
|
3. Test MCP server connection independently
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Version Control
|
||||||
|
|
||||||
|
**Do commit**:
|
||||||
|
|
||||||
|
- `.specify/extensions.yml` (project extension config)
|
||||||
|
- `.specify/extensions/*/jira-config.yml` (project config)
|
||||||
|
|
||||||
|
**Don't commit**:
|
||||||
|
|
||||||
|
- `.specify/extensions/.cache/` (catalog cache)
|
||||||
|
- `.specify/extensions/.backup/` (config backups)
|
||||||
|
- `.specify/extensions/*/*.local.yml` (local overrides)
|
||||||
|
- `.specify/extensions/.registry` (installation state)
|
||||||
|
|
||||||
|
Add to `.gitignore`:
|
||||||
|
|
||||||
|
```gitignore
|
||||||
|
.specify/extensions/.cache/
|
||||||
|
.specify/extensions/.backup/
|
||||||
|
.specify/extensions/*/*.local.yml
|
||||||
|
.specify/extensions/.registry
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Team Workflows
|
||||||
|
|
||||||
|
**For teams**:
|
||||||
|
|
||||||
|
1. Agree on which extensions to use
|
||||||
|
2. Commit extension configuration
|
||||||
|
3. Document extension usage in README
|
||||||
|
4. Keep extensions updated together
|
||||||
|
|
||||||
|
**Example README section**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Extensions
|
||||||
|
|
||||||
|
This project uses:
|
||||||
|
- **jira** (v1.0.0) - Jira integration
|
||||||
|
- Config: `.specify/extensions/jira/jira-config.yml`
|
||||||
|
- Requires: jira-mcp-server
|
||||||
|
|
||||||
|
To install: `specify extension add jira`
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Local Development
|
||||||
|
|
||||||
|
Use local config for development:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .specify/extensions/jira/jira-config.local.yml
|
||||||
|
project:
|
||||||
|
key: "DEVTEST" # Your test project
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
task:
|
||||||
|
custom_fields:
|
||||||
|
customfield_10002: 1 # Lower story points for testing
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Environment-Specific Config
|
||||||
|
|
||||||
|
Use environment variables for CI/CD:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .github/workflows/deploy.yml
|
||||||
|
env:
|
||||||
|
SPECKIT_JIRA_PROJECT_KEY: ${{ secrets.JIRA_PROJECT }}
|
||||||
|
|
||||||
|
- name: Create Jira Issues
|
||||||
|
run: specify extension add jira && ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Extension Updates
|
||||||
|
|
||||||
|
**Check for updates regularly**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Weekly or before major releases
|
||||||
|
specify extension update
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pin versions for stability**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .specify/extensions.yml
|
||||||
|
installed:
|
||||||
|
- id: jira
|
||||||
|
version: "1.0.0" # Pin to specific version
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Minimal Extensions
|
||||||
|
|
||||||
|
Only install extensions you actively use:
|
||||||
|
|
||||||
|
- Reduces complexity
|
||||||
|
- Faster command loading
|
||||||
|
- Less configuration
|
||||||
|
|
||||||
|
### 7. Documentation
|
||||||
|
|
||||||
|
Document extension usage in your project:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# PROJECT.md
|
||||||
|
|
||||||
|
## Working with Jira
|
||||||
|
|
||||||
|
After creating tasks, sync to Jira:
|
||||||
|
1. Run `/speckit.tasks` to generate tasks
|
||||||
|
2. Run `/speckit.jira.specstoissues` to create Jira issues
|
||||||
|
3. Run `/speckit.jira.sync-status` to update status
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Q: Can I use multiple extensions at once?
|
||||||
|
|
||||||
|
**A**: Yes! Extensions are designed to work together. Install as many as you need.
|
||||||
|
|
||||||
|
### Q: Do extensions slow down spec-kit?
|
||||||
|
|
||||||
|
**A**: No. Extensions are loaded on-demand and only when their commands are used.
|
||||||
|
|
||||||
|
### Q: Can I create private extensions?
|
||||||
|
|
||||||
|
**A**: Yes. Install with `--dev` or `--from` and keep private. Public catalog submission is optional.
|
||||||
|
|
||||||
|
### Q: How do I know if an extension is safe?
|
||||||
|
|
||||||
|
**A**: Look for the ✓ Verified badge. Verified extensions are reviewed by maintainers. Always review extension code before installing.
|
||||||
|
|
||||||
|
### Q: Can extensions modify spec-kit core?
|
||||||
|
|
||||||
|
**A**: No. Extensions can only add commands and hooks. They cannot modify core functionality.
|
||||||
|
|
||||||
|
### Q: What happens if two extensions have the same command name?
|
||||||
|
|
||||||
|
**A**: Extensions use namespaced commands (`speckit.{extension}.{command}`), so conflicts are very rare. The extension system will warn you if conflicts occur.
|
||||||
|
|
||||||
|
### Q: Can I contribute to existing extensions?
|
||||||
|
|
||||||
|
**A**: Yes! Most extensions are open source. Check the repository link in `specify extension info {extension}`.
|
||||||
|
|
||||||
|
### Q: How do I report extension bugs?
|
||||||
|
|
||||||
|
**A**: Go to the extension's repository (shown in `specify extension info`) and create an issue.
|
||||||
|
|
||||||
|
### Q: Can extensions work offline?
|
||||||
|
|
||||||
|
**A**: Once installed, extensions work offline. However, some extensions may require internet for their functionality (e.g., Jira requires Jira API access).
|
||||||
|
|
||||||
|
### Q: How do I backup my extension configuration?
|
||||||
|
|
||||||
|
**A**: Extension configs are in `.specify/extensions/{extension}/`. Back up this directory or commit configs to git.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- **Extension Issues**: Report to extension repository (see `specify extension info`)
|
||||||
|
- **Spec Kit Issues**: <https://github.com/statsperform/spec-kit/issues>
|
||||||
|
- **Extension Catalog**: <https://github.com/statsperform/spec-kit/tree/main/extensions>
|
||||||
|
- **Documentation**: See EXTENSION-DEVELOPMENT-GUIDE.md and EXTENSION-PUBLISHING-GUIDE.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last Updated: 2026-01-28*
|
||||||
|
*Spec Kit Version: 0.1.0*
|
||||||
130
extensions/README.md
Normal file
130
extensions/README.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Spec Kit Extensions
|
||||||
|
|
||||||
|
Extension system for [Spec Kit](https://github.com/github/spec-kit) - add new functionality without bloating the core framework.
|
||||||
|
|
||||||
|
## Extension Catalogs
|
||||||
|
|
||||||
|
Spec Kit provides two catalog files with different purposes:
|
||||||
|
|
||||||
|
### Your Catalog (`catalog.json`)
|
||||||
|
|
||||||
|
- **Purpose**: Default upstream catalog of extensions used by the Spec Kit CLI
|
||||||
|
- **Default State**: Empty by design in the upstream project - you or your organization populate a fork/copy with extensions you trust
|
||||||
|
- **Location (upstream)**: `extensions/catalog.json` in the GitHub-hosted spec-kit repo
|
||||||
|
- **CLI Default**: The `specify extension` commands use the upstream catalog URL by default, unless overridden
|
||||||
|
- **Org Catalog**: Point `SPECKIT_CATALOG_URL` at your organization's fork or hosted catalog JSON to use it instead of the upstream default
|
||||||
|
- **Customization**: Copy entries from the community catalog into your org catalog, or add your own extensions directly
|
||||||
|
|
||||||
|
**Example override:**
|
||||||
|
```bash
|
||||||
|
# Override the default upstream catalog with your organization's catalog
|
||||||
|
export SPECKIT_CATALOG_URL="https://your-org.com/spec-kit/catalog.json"
|
||||||
|
specify extension search # Now uses your organization's catalog instead of the upstream default
|
||||||
|
```
|
||||||
|
|
||||||
|
### Community Reference Catalog (`catalog.community.json`)
|
||||||
|
|
||||||
|
- **Purpose**: Browse available community-contributed extensions
|
||||||
|
- **Status**: Active - contains extensions submitted by the community
|
||||||
|
- **Location**: `extensions/catalog.community.json`
|
||||||
|
- **Usage**: Reference catalog for discovering available extensions
|
||||||
|
- **Submission**: Open to community contributions via Pull Request
|
||||||
|
|
||||||
|
**How It Works:**
|
||||||
|
|
||||||
|
## Making Extensions Available
|
||||||
|
|
||||||
|
You control which extensions your team can discover and install:
|
||||||
|
|
||||||
|
### Option 1: Curated Catalog (Recommended for Organizations)
|
||||||
|
|
||||||
|
Populate your `catalog.json` with approved extensions:
|
||||||
|
|
||||||
|
1. **Discover** extensions from various sources:
|
||||||
|
- Browse `catalog.community.json` for community extensions
|
||||||
|
- Find private/internal extensions in your organization's repos
|
||||||
|
- Discover extensions from trusted third parties
|
||||||
|
2. **Review** extensions and choose which ones you want to make available
|
||||||
|
3. **Add** those extension entries to your own `catalog.json`
|
||||||
|
4. **Team members** can now discover and install them:
|
||||||
|
- `specify extension search` shows your curated catalog
|
||||||
|
- `specify extension add <name>` installs from your catalog
|
||||||
|
|
||||||
|
**Benefits**: Full control over available extensions, team consistency, organizational approval workflow
|
||||||
|
|
||||||
|
**Example**: Copy an entry from `catalog.community.json` to your `catalog.json`, then your team can discover and install it by name.
|
||||||
|
|
||||||
|
### Option 2: Direct URLs (For Ad-hoc Use)
|
||||||
|
|
||||||
|
Skip catalog curation - team members install directly using URLs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add --from https://github.com/org/spec-kit-ext/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits**: Quick for one-off testing or private extensions
|
||||||
|
|
||||||
|
**Tradeoff**: Extensions installed this way won't appear in `specify extension search` for other team members unless you also add them to your `catalog.json`.
|
||||||
|
|
||||||
|
## Available Community Extensions
|
||||||
|
|
||||||
|
The following community-contributed extensions are available in [`catalog.community.json`](catalog.community.json):
|
||||||
|
|
||||||
|
| Extension | Purpose | URL |
|
||||||
|
|-----------|---------|-----|
|
||||||
|
| Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) |
|
||||||
|
| Cleanup Extension | Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) |
|
||||||
|
| Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) |
|
||||||
|
| Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) |
|
||||||
|
| Project Health Check | Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git | [spec-kit-doctor](https://github.com/KhawarHabibKhan/spec-kit-doctor) |
|
||||||
|
| Ralph Loop | Autonomous implementation loop using AI agent CLI | [spec-kit-ralph](https://github.com/Rubiss/spec-kit-ralph) |
|
||||||
|
| Retrospective Extension | Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates | [spec-kit-retrospective](https://github.com/emi-dm/spec-kit-retrospective) |
|
||||||
|
| Review Extension | Post-implementation comprehensive code review with specialized agents for code quality, comments, tests, error handling, type design, and simplification | [spec-kit-review](https://github.com/ismaelJimenez/spec-kit-review) |
|
||||||
|
| Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) |
|
||||||
|
| Understanding | Automated requirements quality analysis — 31 deterministic metrics against IEEE/ISO standards with experimental energy-based ambiguity detection | [understanding](https://github.com/Testimonial/understanding) |
|
||||||
|
| V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) |
|
||||||
|
| Verify Extension | Post-implementation quality gate that validates implemented code against specification artifacts | [spec-kit-verify](https://github.com/ismaelJimenez/spec-kit-verify) |
|
||||||
|
|
||||||
|
|
||||||
|
## Adding Your Extension
|
||||||
|
|
||||||
|
### Submission Process
|
||||||
|
|
||||||
|
To add your extension to the community catalog:
|
||||||
|
|
||||||
|
1. **Prepare your extension** following the [Extension Development Guide](EXTENSION-DEVELOPMENT-GUIDE.md)
|
||||||
|
2. **Create a GitHub release** for your extension
|
||||||
|
3. **Submit a Pull Request** that:
|
||||||
|
- Adds your extension to `extensions/catalog.community.json`
|
||||||
|
- Updates this README with your extension in the Available Extensions table
|
||||||
|
4. **Wait for review** - maintainers will review and merge if criteria are met
|
||||||
|
|
||||||
|
See the [Extension Publishing Guide](EXTENSION-PUBLISHING-GUIDE.md) for detailed step-by-step instructions.
|
||||||
|
|
||||||
|
### Submission Checklist
|
||||||
|
|
||||||
|
Before submitting, ensure:
|
||||||
|
|
||||||
|
- ✅ Valid `extension.yml` manifest
|
||||||
|
- ✅ Complete README with installation and usage instructions
|
||||||
|
- ✅ LICENSE file included
|
||||||
|
- ✅ GitHub release created with semantic version (e.g., v1.0.0)
|
||||||
|
- ✅ Extension tested on a real project
|
||||||
|
- ✅ All commands working as documented
|
||||||
|
|
||||||
|
## Installing Extensions
|
||||||
|
Once extensions are available (either in your catalog or via direct URL), install them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From your curated catalog (by name)
|
||||||
|
specify extension search # See what's in your catalog
|
||||||
|
specify extension add <extension-name> # Install by name
|
||||||
|
|
||||||
|
# Direct from URL (bypasses catalog)
|
||||||
|
specify extension add --from https://github.com/<org>/<repo>/archive/refs/tags/<version>.zip
|
||||||
|
|
||||||
|
# List installed extensions
|
||||||
|
specify extension list
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, see the [Extension User Guide](EXTENSION-USER-GUIDE.md).
|
||||||
1933
extensions/RFC-EXTENSION-SYSTEM.md
Normal file
1933
extensions/RFC-EXTENSION-SYSTEM.md
Normal file
File diff suppressed because it is too large
Load Diff
397
extensions/catalog.community.json
Normal file
397
extensions/catalog.community.json
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-03-13T12:00:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
||||||
|
"extensions": {
|
||||||
|
"azure-devops": {
|
||||||
|
"name": "Azure DevOps Integration",
|
||||||
|
"id": "azure-devops",
|
||||||
|
"description": "Sync user stories and tasks to Azure DevOps work items using OAuth authentication.",
|
||||||
|
"author": "pragya247",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/pragya247/spec-kit-azure-devops/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/pragya247/spec-kit-azure-devops",
|
||||||
|
"homepage": "https://github.com/pragya247/spec-kit-azure-devops",
|
||||||
|
"documentation": "https://github.com/pragya247/spec-kit-azure-devops/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/pragya247/spec-kit-azure-devops/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "az",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 1,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"azure",
|
||||||
|
"devops",
|
||||||
|
"project-management",
|
||||||
|
"work-items",
|
||||||
|
"issue-tracking"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-03T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-03T00:00:00Z"
|
||||||
|
},
|
||||||
|
"cleanup": {
|
||||||
|
"name": "Cleanup Extension",
|
||||||
|
"id": "cleanup",
|
||||||
|
"description": "Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues.",
|
||||||
|
"author": "dsrednicki",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/dsrednicki/spec-kit-cleanup/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/dsrednicki/spec-kit-cleanup",
|
||||||
|
"homepage": "https://github.com/dsrednicki/spec-kit-cleanup",
|
||||||
|
"documentation": "https://github.com/dsrednicki/spec-kit-cleanup/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/dsrednicki/spec-kit-cleanup/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 1,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"quality",
|
||||||
|
"tech-debt",
|
||||||
|
"review",
|
||||||
|
"cleanup",
|
||||||
|
"scout-rule"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-02-22T00:00:00Z",
|
||||||
|
"updated_at": "2026-02-22T00:00:00Z"
|
||||||
|
},
|
||||||
|
"doctor": {
|
||||||
|
"name": "Project Health Check",
|
||||||
|
"id": "doctor",
|
||||||
|
"description": "Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git.",
|
||||||
|
"author": "KhawarHabibKhan",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/KhawarHabibKhan/spec-kit-doctor/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/KhawarHabibKhan/spec-kit-doctor",
|
||||||
|
"homepage": "https://github.com/KhawarHabibKhan/spec-kit-doctor",
|
||||||
|
"documentation": "https://github.com/KhawarHabibKhan/spec-kit-doctor/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/KhawarHabibKhan/spec-kit-doctor/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 1,
|
||||||
|
"hooks": 0
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"diagnostics",
|
||||||
|
"health-check",
|
||||||
|
"validation",
|
||||||
|
"project-structure"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-13T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-13T00:00:00Z"
|
||||||
|
},
|
||||||
|
"fleet": {
|
||||||
|
"name": "Fleet Orchestrator",
|
||||||
|
"id": "fleet",
|
||||||
|
"description": "Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases.",
|
||||||
|
"author": "sharathsatish",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/sharathsatish/spec-kit-fleet/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/sharathsatish/spec-kit-fleet",
|
||||||
|
"homepage": "https://github.com/sharathsatish/spec-kit-fleet",
|
||||||
|
"documentation": "https://github.com/sharathsatish/spec-kit-fleet/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/sharathsatish/spec-kit-fleet/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 2,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": ["orchestration", "workflow", "human-in-the-loop", "parallel"],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-06T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-06T00:00:00Z"
|
||||||
|
},
|
||||||
|
"jira": {
|
||||||
|
"name": "Jira Integration",
|
||||||
|
"id": "jira",
|
||||||
|
"description": "Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support.",
|
||||||
|
"author": "mbachorik",
|
||||||
|
"version": "2.1.0",
|
||||||
|
"download_url": "https://github.com/mbachorik/spec-kit-jira/archive/refs/tags/v2.1.0.zip",
|
||||||
|
"repository": "https://github.com/mbachorik/spec-kit-jira",
|
||||||
|
"homepage": "https://github.com/mbachorik/spec-kit-jira",
|
||||||
|
"documentation": "https://github.com/mbachorik/spec-kit-jira/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/mbachorik/spec-kit-jira/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 3,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"issue-tracking",
|
||||||
|
"jira",
|
||||||
|
"atlassian",
|
||||||
|
"project-management"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-05T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-05T00:00:00Z"
|
||||||
|
},
|
||||||
|
"ralph": {
|
||||||
|
"name": "Ralph Loop",
|
||||||
|
"id": "ralph",
|
||||||
|
"description": "Autonomous implementation loop using AI agent CLI.",
|
||||||
|
"author": "Rubiss",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/Rubiss/spec-kit-ralph/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/Rubiss/spec-kit-ralph",
|
||||||
|
"homepage": "https://github.com/Rubiss/spec-kit-ralph",
|
||||||
|
"documentation": "https://github.com/Rubiss/spec-kit-ralph/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/Rubiss/spec-kit-ralph/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "copilot",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "git",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 2,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": ["implementation", "automation", "loop", "copilot"],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-09T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-09T00:00:00Z"
|
||||||
|
},
|
||||||
|
"retrospective": {
|
||||||
|
"name": "Retrospective Extension",
|
||||||
|
"id": "retrospective",
|
||||||
|
"description": "Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates.",
|
||||||
|
"author": "emi-dm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/emi-dm/spec-kit-retrospective/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/emi-dm/spec-kit-retrospective",
|
||||||
|
"homepage": "https://github.com/emi-dm/spec-kit-retrospective",
|
||||||
|
"documentation": "https://github.com/emi-dm/spec-kit-retrospective/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/emi-dm/spec-kit-retrospective/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 1,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"retrospective",
|
||||||
|
"spec-drift",
|
||||||
|
"quality",
|
||||||
|
"analysis",
|
||||||
|
"governance"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-02-24T00:00:00Z",
|
||||||
|
"updated_at": "2026-02-24T00:00:00Z"
|
||||||
|
},
|
||||||
|
"review": {
|
||||||
|
"name": "Review Extension",
|
||||||
|
"id": "review",
|
||||||
|
"description": "Post-implementation comprehensive code review with specialized agents for code quality, comments, tests, error handling, type design, and simplification.",
|
||||||
|
"author": "ismaelJimenez",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/ismaelJimenez/spec-kit-review/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/ismaelJimenez/spec-kit-review",
|
||||||
|
"homepage": "https://github.com/ismaelJimenez/spec-kit-review",
|
||||||
|
"documentation": "https://github.com/ismaelJimenez/spec-kit-review/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/ismaelJimenez/spec-kit-review/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 7,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": ["code-review", "quality", "review", "testing", "error-handling", "type-design", "simplification"],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-06T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-06T00:00:00Z"
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"name": "Spec Sync",
|
||||||
|
"id": "sync",
|
||||||
|
"description": "Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval.",
|
||||||
|
"author": "bgervin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"download_url": "https://github.com/bgervin/spec-kit-sync/archive/refs/tags/v0.1.0.zip",
|
||||||
|
"repository": "https://github.com/bgervin/spec-kit-sync",
|
||||||
|
"homepage": "https://github.com/bgervin/spec-kit-sync",
|
||||||
|
"documentation": "https://github.com/bgervin/spec-kit-sync/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/bgervin/spec-kit-sync/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 5,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"sync",
|
||||||
|
"drift",
|
||||||
|
"validation",
|
||||||
|
"bidirectional",
|
||||||
|
"backfill"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-02T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-02T00:00:00Z"
|
||||||
|
},
|
||||||
|
"understanding": {
|
||||||
|
"name": "Understanding",
|
||||||
|
"id": "understanding",
|
||||||
|
"description": "Automated requirements quality analysis — validates specs against IEEE/ISO standards using 31 deterministic metrics. Catches ambiguity, missing testability, and structural issues before they reach implementation. Includes experimental energy-based ambiguity detection using local LM token perplexity.",
|
||||||
|
"author": "Ladislav Bihari",
|
||||||
|
"version": "3.4.0",
|
||||||
|
"download_url": "https://github.com/Testimonial/understanding/archive/refs/tags/v3.4.0.zip",
|
||||||
|
"repository": "https://github.com/Testimonial/understanding",
|
||||||
|
"homepage": "https://github.com/Testimonial/understanding",
|
||||||
|
"documentation": "https://github.com/Testimonial/understanding/blob/main/extension/README.md",
|
||||||
|
"changelog": "https://github.com/Testimonial/understanding/blob/main/extension/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0",
|
||||||
|
"tools": [
|
||||||
|
{
|
||||||
|
"name": "understanding",
|
||||||
|
"version": ">=3.4.0",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 3,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"quality",
|
||||||
|
"metrics",
|
||||||
|
"requirements",
|
||||||
|
"validation",
|
||||||
|
"readability",
|
||||||
|
"IEEE-830",
|
||||||
|
"ISO-29148"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-07T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-07T00:00:00Z"
|
||||||
|
},
|
||||||
|
"v-model": {
|
||||||
|
"name": "V-Model Extension Pack",
|
||||||
|
"id": "v-model",
|
||||||
|
"description": "Enforces V-Model paired generation of development specs and test specs with full traceability.",
|
||||||
|
"author": "leocamello",
|
||||||
|
"version": "0.4.0",
|
||||||
|
"download_url": "https://github.com/leocamello/spec-kit-v-model/archive/refs/tags/v0.4.0.zip",
|
||||||
|
"repository": "https://github.com/leocamello/spec-kit-v-model",
|
||||||
|
"homepage": "https://github.com/leocamello/spec-kit-v-model",
|
||||||
|
"documentation": "https://github.com/leocamello/spec-kit-v-model/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/leocamello/spec-kit-v-model/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 9,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"v-model",
|
||||||
|
"traceability",
|
||||||
|
"testing",
|
||||||
|
"compliance",
|
||||||
|
"safety-critical"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-02-20T00:00:00Z",
|
||||||
|
"updated_at": "2026-02-22T00:00:00Z"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"name": "Verify Extension",
|
||||||
|
"id": "verify",
|
||||||
|
"description": "Post-implementation quality gate that validates implemented code against specification artifacts.",
|
||||||
|
"author": "ismaelJimenez",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/ismaelJimenez/spec-kit-verify/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/ismaelJimenez/spec-kit-verify",
|
||||||
|
"homepage": "https://github.com/ismaelJimenez/spec-kit-verify",
|
||||||
|
"documentation": "https://github.com/ismaelJimenez/spec-kit-verify/blob/main/README.md",
|
||||||
|
"changelog": "https://github.com/ismaelJimenez/spec-kit-verify/blob/main/CHANGELOG.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"commands": 1,
|
||||||
|
"hooks": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"verification",
|
||||||
|
"quality-gate",
|
||||||
|
"implementation",
|
||||||
|
"spec-adherence",
|
||||||
|
"compliance"
|
||||||
|
],
|
||||||
|
"verified": false,
|
||||||
|
"downloads": 0,
|
||||||
|
"stars": 0,
|
||||||
|
"created_at": "2026-03-03T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-03T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
extensions/catalog.json
Normal file
21
extensions/catalog.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-03-10T00:00:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.json",
|
||||||
|
"extensions": {
|
||||||
|
"selftest": {
|
||||||
|
"name": "Spec Kit Self-Test Utility",
|
||||||
|
"id": "selftest",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Verifies catalog extensions by programmatically walking through the discovery, installation, and registration lifecycle.",
|
||||||
|
"author": "spec-kit-core",
|
||||||
|
"repository": "https://github.com/github/spec-kit",
|
||||||
|
"download_url": "https://github.com/github/spec-kit/releases/download/selftest-v1.0.0/selftest.zip",
|
||||||
|
"tags": [
|
||||||
|
"testing",
|
||||||
|
"core",
|
||||||
|
"utility"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
extensions/selftest/commands/selftest.md
Normal file
69
extensions/selftest/commands/selftest.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
description: "Validate the lifecycle of an extension from the catalog."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Extension Self-Test: `$ARGUMENTS`
|
||||||
|
|
||||||
|
This command drives a self-test simulating the developer experience with the `$ARGUMENTS` extension.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Validate the end-to-end lifecycle (discovery, installation, registration) for the extension: `$ARGUMENTS`.
|
||||||
|
If `$ARGUMENTS` is empty, you must tell the user to provide an extension name, for example: `/speckit.selftest.extension linear`.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Step 1: Catalog Discovery Validation
|
||||||
|
|
||||||
|
Check if the extension exists in the Spec Kit catalog.
|
||||||
|
Execute this command and verify that it completes successfully and that the returned extension ID exactly matches `$ARGUMENTS`. If the command fails or the ID does not match `$ARGUMENTS`, fail the test.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension info "$ARGUMENTS"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Simulate Installation
|
||||||
|
|
||||||
|
First, try to add the extension to the current workspace configuration directly. If the catalog provides the extension as `install_allowed: false` (discovery-only), this step is *expected* to fail.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add "$ARGUMENTS"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, simulate adding the extension by installing it from its catalog download URL, which should bypass the restriction.
|
||||||
|
Obtain the extension's `download_url` from the catalog metadata (for example, via a catalog info command or UI), then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify extension add "$ARGUMENTS" --from "<download_url>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Registration Verification
|
||||||
|
|
||||||
|
Once the `add` command completes, verify the installation by checking the project configuration.
|
||||||
|
Use terminal tools (like `cat`) to verify that the following file contains a record for `$ARGUMENTS`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat .specify/extensions/.registry/$ARGUMENTS.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Verification Report
|
||||||
|
|
||||||
|
Analyze the standard output of the three steps.
|
||||||
|
Generate a terminal-style test output format detailing the results of discovery, installation, and registration. Return this directly to the user.
|
||||||
|
|
||||||
|
Example output format:
|
||||||
|
```text
|
||||||
|
============================= test session starts ==============================
|
||||||
|
collected 3 items
|
||||||
|
|
||||||
|
test_selftest_discovery.py::test_catalog_search [PASS/FAIL]
|
||||||
|
Details: [Provide execution result of specify extension search]
|
||||||
|
|
||||||
|
test_selftest_installation.py::test_extension_add [PASS/FAIL]
|
||||||
|
Details: [Provide execution result of specify extension add]
|
||||||
|
|
||||||
|
test_selftest_registration.py::test_config_verification [PASS/FAIL]
|
||||||
|
Details: [Provide execution result of registry record verification]
|
||||||
|
|
||||||
|
============================== [X] passed in ... ==============================
|
||||||
|
```
|
||||||
16
extensions/selftest/extension.yml
Normal file
16
extensions/selftest/extension.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
schema_version: "1.0"
|
||||||
|
extension:
|
||||||
|
id: selftest
|
||||||
|
name: Spec Kit Self-Test Utility
|
||||||
|
version: 1.0.0
|
||||||
|
description: Verifies catalog extensions by programmatically walking through the discovery, installation, and registration lifecycle.
|
||||||
|
author: spec-kit-core
|
||||||
|
repository: https://github.com/github/spec-kit
|
||||||
|
license: MIT
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.2.0"
|
||||||
|
provides:
|
||||||
|
commands:
|
||||||
|
- name: speckit.selftest.extension
|
||||||
|
file: commands/selftest.md
|
||||||
|
description: Validate the lifecycle of an extension from the catalog.
|
||||||
39
extensions/template/.gitignore
vendored
Normal file
39
extensions/template/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Local configuration overrides
|
||||||
|
*-config.local.yml
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
.cache/
|
||||||
39
extensions/template/CHANGELOG.md
Normal file
39
extensions/template/CHANGELOG.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this extension will be documented in this file.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Planned
|
||||||
|
|
||||||
|
- Feature ideas for future versions
|
||||||
|
- Enhancements
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
|
## [1.0.0] - YYYY-MM-DD
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Initial release of extension
|
||||||
|
- Command: `/speckit.my-extension.example` - Example command functionality
|
||||||
|
- Configuration system with template
|
||||||
|
- Documentation and examples
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature 1 description
|
||||||
|
- Feature 2 description
|
||||||
|
- Feature 3 description
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Spec Kit: >=0.1.0
|
||||||
|
- External dependencies (if any)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/your-org/spec-kit-my-extension/compare/v1.0.0...HEAD
|
||||||
|
[1.0.0]: https://github.com/your-org/spec-kit-my-extension/releases/tag/v1.0.0
|
||||||
158
extensions/template/EXAMPLE-README.md
Normal file
158
extensions/template/EXAMPLE-README.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# EXAMPLE: Extension README
|
||||||
|
|
||||||
|
This is an example of what your extension README should look like after customization.
|
||||||
|
**Delete this file and replace README.md with content similar to this.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# My Extension
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Replace with your extension description -->
|
||||||
|
|
||||||
|
Brief description of what your extension does and why it's useful.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: List key features -->
|
||||||
|
|
||||||
|
- Feature 1: Description
|
||||||
|
- Feature 2: Description
|
||||||
|
- Feature 3: Description
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install from catalog
|
||||||
|
specify extension add my-extension
|
||||||
|
|
||||||
|
# Or install from local development directory
|
||||||
|
specify extension add --dev /path/to/my-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Create configuration file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .specify/extensions/my-extension/config-template.yml \
|
||||||
|
.specify/extensions/my-extension/my-extension-config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Edit configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim .specify/extensions/my-extension/my-extension-config.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Set required values:
|
||||||
|
<!-- CUSTOMIZE: List required configuration -->
|
||||||
|
```yaml
|
||||||
|
connection:
|
||||||
|
url: "https://api.example.com"
|
||||||
|
api_key: "your-api-key"
|
||||||
|
|
||||||
|
project:
|
||||||
|
id: "your-project-id"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add usage examples -->
|
||||||
|
|
||||||
|
### Command: example
|
||||||
|
|
||||||
|
Description of what this command does.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In Claude Code
|
||||||
|
> /speckit.my-extension.example
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prerequisites**:
|
||||||
|
|
||||||
|
- Prerequisite 1
|
||||||
|
- Prerequisite 2
|
||||||
|
|
||||||
|
**Output**:
|
||||||
|
|
||||||
|
- What this command produces
|
||||||
|
- Where results are saved
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Document all configuration options -->
|
||||||
|
|
||||||
|
### Connection Settings
|
||||||
|
|
||||||
|
| Setting | Type | Required | Description |
|
||||||
|
|---------|------|----------|-------------|
|
||||||
|
| `connection.url` | string | Yes | API endpoint URL |
|
||||||
|
| `connection.api_key` | string | Yes | API authentication key |
|
||||||
|
|
||||||
|
### Project Settings
|
||||||
|
|
||||||
|
| Setting | Type | Required | Description |
|
||||||
|
|---------|------|----------|-------------|
|
||||||
|
| `project.id` | string | Yes | Project identifier |
|
||||||
|
| `project.workspace` | string | No | Workspace or organization |
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Override configuration with environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Override connection settings
|
||||||
|
export SPECKIT_MY_EXTENSION_CONNECTION_URL="https://custom-api.com"
|
||||||
|
export SPECKIT_MY_EXTENSION_CONNECTION_API_KEY="custom-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add real-world examples -->
|
||||||
|
|
||||||
|
### Example 1: Basic Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Step 1: Create specification
|
||||||
|
> /speckit.spec
|
||||||
|
|
||||||
|
# Step 2: Generate tasks
|
||||||
|
> /speckit.tasks
|
||||||
|
|
||||||
|
# Step 3: Use extension
|
||||||
|
> /speckit.my-extension.example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add common issues -->
|
||||||
|
|
||||||
|
### Issue: Configuration not found
|
||||||
|
|
||||||
|
**Solution**: Create config from template (see Configuration section)
|
||||||
|
|
||||||
|
### Issue: Command not available
|
||||||
|
|
||||||
|
**Solutions**:
|
||||||
|
|
||||||
|
1. Check extension is installed: `specify extension list`
|
||||||
|
2. Restart AI agent
|
||||||
|
3. Reinstall extension
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - see LICENSE file
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- **Issues**: <https://github.com/your-org/spec-kit-my-extension/issues>
|
||||||
|
- **Spec Kit Docs**: <https://github.com/statsperform/spec-kit>
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Extension Version: 1.0.0*
|
||||||
|
*Spec Kit: >=0.1.0*
|
||||||
21
extensions/template/LICENSE
Normal file
21
extensions/template/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 [Your Name or Organization]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
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.
|
||||||
79
extensions/template/README.md
Normal file
79
extensions/template/README.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Extension Template
|
||||||
|
|
||||||
|
Starter template for creating a Spec Kit extension.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Copy this template**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp -r extensions/template my-extension
|
||||||
|
cd my-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Customize `extension.yml`**:
|
||||||
|
- Change extension ID, name, description
|
||||||
|
- Update author and repository
|
||||||
|
- Define your commands
|
||||||
|
|
||||||
|
3. **Create commands**:
|
||||||
|
- Add command files in `commands/` directory
|
||||||
|
- Use Markdown format with YAML frontmatter
|
||||||
|
|
||||||
|
4. **Create config template**:
|
||||||
|
- Define configuration options
|
||||||
|
- Document all settings
|
||||||
|
|
||||||
|
5. **Write documentation**:
|
||||||
|
- Update README.md with usage instructions
|
||||||
|
- Add examples
|
||||||
|
|
||||||
|
6. **Test locally**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/spec-kit-project
|
||||||
|
specify extension add --dev /path/to/my-extension
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **Publish** (optional):
|
||||||
|
- Create GitHub repository
|
||||||
|
- Create release
|
||||||
|
- Submit to catalog (see EXTENSION-PUBLISHING-GUIDE.md)
|
||||||
|
|
||||||
|
## Files in This Template
|
||||||
|
|
||||||
|
- `extension.yml` - Extension manifest (CUSTOMIZE THIS)
|
||||||
|
- `config-template.yml` - Configuration template (CUSTOMIZE THIS)
|
||||||
|
- `commands/example.md` - Example command (REPLACE THIS)
|
||||||
|
- `README.md` - Extension documentation (REPLACE THIS)
|
||||||
|
- `LICENSE` - MIT License (REVIEW THIS)
|
||||||
|
- `CHANGELOG.md` - Version history (UPDATE THIS)
|
||||||
|
- `.gitignore` - Git ignore rules
|
||||||
|
|
||||||
|
## Customization Checklist
|
||||||
|
|
||||||
|
- [ ] Update `extension.yml` with your extension details
|
||||||
|
- [ ] Change extension ID to your extension name
|
||||||
|
- [ ] Update author information
|
||||||
|
- [ ] Define your commands
|
||||||
|
- [ ] Create command files in `commands/`
|
||||||
|
- [ ] Update config template
|
||||||
|
- [ ] Write README with usage instructions
|
||||||
|
- [ ] Add examples
|
||||||
|
- [ ] Update LICENSE if needed
|
||||||
|
- [ ] Test extension locally
|
||||||
|
- [ ] Create git repository
|
||||||
|
- [ ] Create first release
|
||||||
|
|
||||||
|
## Need Help?
|
||||||
|
|
||||||
|
- **Development Guide**: See EXTENSION-DEVELOPMENT-GUIDE.md
|
||||||
|
- **API Reference**: See EXTENSION-API-REFERENCE.md
|
||||||
|
- **Publishing Guide**: See EXTENSION-PUBLISHING-GUIDE.md
|
||||||
|
- **User Guide**: See EXTENSION-USER-GUIDE.md
|
||||||
|
|
||||||
|
## Template Version
|
||||||
|
|
||||||
|
- Version: 1.0.0
|
||||||
|
- Last Updated: 2026-01-28
|
||||||
|
- Compatible with Spec Kit: >=0.1.0
|
||||||
210
extensions/template/commands/example.md
Normal file
210
extensions/template/commands/example.md
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
description: "Example command that demonstrates extension functionality"
|
||||||
|
# CUSTOMIZE: List MCP tools this command uses
|
||||||
|
tools:
|
||||||
|
- 'example-mcp-server/example_tool'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Example Command
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Replace this entire file with your command documentation -->
|
||||||
|
|
||||||
|
This is an example command that demonstrates how to create commands for Spec Kit extensions.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Describe what this command does and when to use it.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
List requirements before using this command:
|
||||||
|
|
||||||
|
1. Prerequisite 1 (e.g., "MCP server configured")
|
||||||
|
2. Prerequisite 2 (e.g., "Configuration file exists")
|
||||||
|
3. Prerequisite 3 (e.g., "Valid API credentials")
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### Step 1: Load Configuration
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Replace with your actual steps -->
|
||||||
|
|
||||||
|
Load extension configuration from the project:
|
||||||
|
|
||||||
|
``bash
|
||||||
|
config_file=".specify/extensions/my-extension/my-extension-config.yml"
|
||||||
|
|
||||||
|
if [ ! -f "$config_file" ]; then
|
||||||
|
echo "❌ Error: Configuration not found at $config_file"
|
||||||
|
echo "Run 'specify extension add my-extension' to install and configure"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read configuration values
|
||||||
|
|
||||||
|
setting_value=$(yq eval '.settings.key' "$config_file")
|
||||||
|
|
||||||
|
# Apply environment variable overrides
|
||||||
|
|
||||||
|
setting_value="${SPECKIT_MY_EXTENSION_KEY:-$setting_value}"
|
||||||
|
|
||||||
|
# Validate configuration
|
||||||
|
|
||||||
|
if [ -z "$setting_value" ]; then
|
||||||
|
echo "❌ Error: Configuration value not set"
|
||||||
|
echo "Edit $config_file and set 'settings.key'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "📋 Configuration loaded: $setting_value"
|
||||||
|
``
|
||||||
|
|
||||||
|
### Step 2: Perform Main Action
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Replace with your command logic -->
|
||||||
|
|
||||||
|
Describe what this step does:
|
||||||
|
|
||||||
|
``markdown
|
||||||
|
Use MCP tools to perform the main action:
|
||||||
|
|
||||||
|
- Tool: example-mcp-server example_tool
|
||||||
|
- Parameters: { "key": "$setting_value" }
|
||||||
|
|
||||||
|
This calls the MCP server tool to execute the operation.
|
||||||
|
``
|
||||||
|
|
||||||
|
### Step 3: Process Results
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add more steps as needed -->
|
||||||
|
|
||||||
|
Process the results and provide output:
|
||||||
|
|
||||||
|
`` bash
|
||||||
|
echo ""
|
||||||
|
echo "✅ Command completed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Results:"
|
||||||
|
echo " • Item 1: Value"
|
||||||
|
echo " • Item 2: Value"
|
||||||
|
echo ""
|
||||||
|
``
|
||||||
|
|
||||||
|
### Step 4: Save Output (Optional)
|
||||||
|
|
||||||
|
Save results to a file if needed:
|
||||||
|
|
||||||
|
``bash
|
||||||
|
output_file=".specify/my-extension-output.json"
|
||||||
|
|
||||||
|
cat > "$output_file" <<EOF
|
||||||
|
{
|
||||||
|
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
||||||
|
"setting": "$setting_value",
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "💾 Output saved to $output_file"
|
||||||
|
``
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Document configuration options -->
|
||||||
|
|
||||||
|
This command uses the following configuration from `my-extension-config.yml`:
|
||||||
|
|
||||||
|
- **settings.key**: Description of what this setting does
|
||||||
|
- Type: string
|
||||||
|
- Required: Yes
|
||||||
|
- Example: `"example-value"`
|
||||||
|
|
||||||
|
- **settings.another_key**: Description of another setting
|
||||||
|
- Type: boolean
|
||||||
|
- Required: No
|
||||||
|
- Default: `false`
|
||||||
|
- Example: `true`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Document environment variable overrides -->
|
||||||
|
|
||||||
|
Configuration can be overridden with environment variables:
|
||||||
|
|
||||||
|
- `SPECKIT_MY_EXTENSION_KEY` - Overrides `settings.key`
|
||||||
|
- `SPECKIT_MY_EXTENSION_ANOTHER_KEY` - Overrides `settings.another_key`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
``bash
|
||||||
|
export SPECKIT_MY_EXTENSION_KEY="override-value"
|
||||||
|
``
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add common issues and solutions -->
|
||||||
|
|
||||||
|
### "Configuration not found"
|
||||||
|
|
||||||
|
**Solution**: Install the extension and create configuration:
|
||||||
|
``bash
|
||||||
|
specify extension add my-extension
|
||||||
|
cp .specify/extensions/my-extension/config-template.yml \
|
||||||
|
.specify/extensions/my-extension/my-extension-config.yml
|
||||||
|
``
|
||||||
|
|
||||||
|
### "MCP tool not available"
|
||||||
|
|
||||||
|
**Solution**: Ensure MCP server is configured in your AI agent settings.
|
||||||
|
|
||||||
|
### "Permission denied"
|
||||||
|
|
||||||
|
**Solution**: Check credentials and permissions in the external service.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add helpful notes and tips -->
|
||||||
|
|
||||||
|
- This command requires an active connection to the external service
|
||||||
|
- Results are cached for performance
|
||||||
|
- Re-run the command to refresh data
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<!-- CUSTOMIZE: Add usage examples -->
|
||||||
|
|
||||||
|
### Example 1: Basic Usage
|
||||||
|
|
||||||
|
``bash
|
||||||
|
|
||||||
|
# Run with default configuration
|
||||||
|
>
|
||||||
|
> /speckit.my-extension.example
|
||||||
|
``
|
||||||
|
|
||||||
|
### Example 2: With Environment Override
|
||||||
|
|
||||||
|
``bash
|
||||||
|
|
||||||
|
# Override configuration with environment variable
|
||||||
|
|
||||||
|
export SPECKIT_MY_EXTENSION_KEY="custom-value"
|
||||||
|
> /speckit.my-extension.example
|
||||||
|
``
|
||||||
|
|
||||||
|
### Example 3: After Core Command
|
||||||
|
|
||||||
|
``bash
|
||||||
|
|
||||||
|
# Use as part of a workflow
|
||||||
|
>
|
||||||
|
> /speckit.tasks
|
||||||
|
> /speckit.my-extension.example
|
||||||
|
``
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*For more information, see the extension README or run `specify extension info my-extension`*
|
||||||
75
extensions/template/config-template.yml
Normal file
75
extensions/template/config-template.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Extension Configuration Template
|
||||||
|
# Copy this to my-extension-config.yml and customize for your project
|
||||||
|
|
||||||
|
# CUSTOMIZE: Add your configuration sections below
|
||||||
|
|
||||||
|
# Example: Connection settings
|
||||||
|
connection:
|
||||||
|
# URL to external service
|
||||||
|
url: "" # REQUIRED: e.g., "https://api.example.com"
|
||||||
|
|
||||||
|
# API key or token
|
||||||
|
api_key: "" # REQUIRED: Your API key
|
||||||
|
|
||||||
|
# Example: Project settings
|
||||||
|
project:
|
||||||
|
# Project identifier
|
||||||
|
id: "" # REQUIRED: e.g., "my-project"
|
||||||
|
|
||||||
|
# Workspace or organization
|
||||||
|
workspace: "" # OPTIONAL: e.g., "my-org"
|
||||||
|
|
||||||
|
# Example: Feature flags
|
||||||
|
features:
|
||||||
|
# Enable/disable main functionality
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Automatic synchronization
|
||||||
|
auto_sync: false
|
||||||
|
|
||||||
|
# Verbose logging
|
||||||
|
verbose: false
|
||||||
|
|
||||||
|
# Example: Default values
|
||||||
|
defaults:
|
||||||
|
# Labels to apply
|
||||||
|
labels: [] # e.g., ["automated", "spec-kit"]
|
||||||
|
|
||||||
|
# Priority level
|
||||||
|
priority: "medium" # Options: "low", "medium", "high"
|
||||||
|
|
||||||
|
# Assignee
|
||||||
|
assignee: "" # OPTIONAL: Default assignee
|
||||||
|
|
||||||
|
# Example: Field mappings
|
||||||
|
# Map internal names to external field IDs
|
||||||
|
field_mappings:
|
||||||
|
# Example mappings
|
||||||
|
# internal_field: "external_field_id"
|
||||||
|
# status: "customfield_10001"
|
||||||
|
|
||||||
|
# Example: Advanced settings
|
||||||
|
advanced:
|
||||||
|
# Timeout in seconds
|
||||||
|
timeout: 30
|
||||||
|
|
||||||
|
# Retry attempts
|
||||||
|
retry_count: 3
|
||||||
|
|
||||||
|
# Cache duration in seconds
|
||||||
|
cache_duration: 3600
|
||||||
|
|
||||||
|
# Environment Variable Overrides:
|
||||||
|
# You can override any setting with environment variables using this pattern:
|
||||||
|
# SPECKIT_MY_EXTENSION_{SECTION}_{KEY}
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# - SPECKIT_MY_EXTENSION_CONNECTION_API_KEY: Override connection.api_key
|
||||||
|
# - SPECKIT_MY_EXTENSION_PROJECT_ID: Override project.id
|
||||||
|
# - SPECKIT_MY_EXTENSION_FEATURES_ENABLED: Override features.enabled
|
||||||
|
#
|
||||||
|
# Note: Use uppercase and replace dots with underscores
|
||||||
|
|
||||||
|
# Local Overrides:
|
||||||
|
# For local development, create my-extension-config.local.yml (gitignored)
|
||||||
|
# to override settings without affecting the team configuration
|
||||||
97
extensions/template/extension.yml
Normal file
97
extensions/template/extension.yml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
extension:
|
||||||
|
# CUSTOMIZE: Change 'my-extension' to your extension ID (lowercase, hyphen-separated)
|
||||||
|
id: "my-extension"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Human-readable name for your extension
|
||||||
|
name: "My Extension"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Update version when releasing (semantic versioning: X.Y.Z)
|
||||||
|
version: "1.0.0"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Brief description (under 200 characters)
|
||||||
|
description: "Brief description of what your extension does"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Your name or organization name
|
||||||
|
author: "Your Name"
|
||||||
|
|
||||||
|
# CUSTOMIZE: GitHub repository URL (create before publishing)
|
||||||
|
repository: "https://github.com/your-org/spec-kit-my-extension"
|
||||||
|
|
||||||
|
# REVIEW: License (MIT is recommended for open source)
|
||||||
|
license: "MIT"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Extension homepage (can be same as repository)
|
||||||
|
homepage: "https://github.com/your-org/spec-kit-my-extension"
|
||||||
|
|
||||||
|
# Requirements for this extension
|
||||||
|
requires:
|
||||||
|
# CUSTOMIZE: Minimum spec-kit version required
|
||||||
|
# Use >=X.Y.Z for minimum version
|
||||||
|
# Use >=X.Y.Z,<Y.0.0 for version range
|
||||||
|
speckit_version: ">=0.1.0"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Add MCP tools or other dependencies
|
||||||
|
# Remove if no external tools required
|
||||||
|
tools:
|
||||||
|
- name: "example-mcp-server"
|
||||||
|
version: ">=1.0.0"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
# Commands provided by this extension
|
||||||
|
provides:
|
||||||
|
commands:
|
||||||
|
# CUSTOMIZE: Define your commands
|
||||||
|
# Pattern: speckit.{extension-id}.{command-name}
|
||||||
|
- name: "speckit.my-extension.example"
|
||||||
|
file: "commands/example.md"
|
||||||
|
description: "Example command that demonstrates functionality"
|
||||||
|
# Optional: Add aliases for shorter command names
|
||||||
|
aliases: ["speckit.example"]
|
||||||
|
|
||||||
|
# ADD MORE COMMANDS: Copy this block for each command
|
||||||
|
# - name: "speckit.my-extension.another-command"
|
||||||
|
# file: "commands/another-command.md"
|
||||||
|
# description: "Another command"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Define configuration files
|
||||||
|
config:
|
||||||
|
- name: "my-extension-config.yml"
|
||||||
|
template: "config-template.yml"
|
||||||
|
description: "Extension configuration"
|
||||||
|
required: true # Set to false if config is optional
|
||||||
|
|
||||||
|
# CUSTOMIZE: Define hooks (optional)
|
||||||
|
# Remove if no hooks needed
|
||||||
|
hooks:
|
||||||
|
# Hook that runs after /speckit.tasks
|
||||||
|
after_tasks:
|
||||||
|
command: "speckit.my-extension.example"
|
||||||
|
optional: true # User will be prompted
|
||||||
|
prompt: "Run example command?"
|
||||||
|
description: "Demonstrates hook functionality"
|
||||||
|
condition: null # Future: conditional execution
|
||||||
|
|
||||||
|
# ADD MORE HOOKS: Copy this block for other events
|
||||||
|
# after_implement:
|
||||||
|
# command: "speckit.my-extension.another"
|
||||||
|
# optional: false # Auto-execute without prompting
|
||||||
|
# description: "Runs automatically after implementation"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Add relevant tags (2-5 recommended)
|
||||||
|
# Used for discovery in catalog
|
||||||
|
tags:
|
||||||
|
- "example"
|
||||||
|
- "template"
|
||||||
|
# ADD MORE: "category", "tool-name", etc.
|
||||||
|
|
||||||
|
# CUSTOMIZE: Default configuration values (optional)
|
||||||
|
# These are merged with user config
|
||||||
|
defaults:
|
||||||
|
# Example default values
|
||||||
|
feature:
|
||||||
|
enabled: true
|
||||||
|
auto_sync: false
|
||||||
|
|
||||||
|
# ADD MORE: Any default settings for your extension
|
||||||
54
newsletters/2026-February.md
Normal file
54
newsletters/2026-February.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Spec Kit - February 2026 Newsletter
|
||||||
|
|
||||||
|
This edition covers Spec Kit activity in February 2026. Versions v0.1.7 through v0.1.13 shipped during the month, addressing bugs and adding features including a dual-catalog extension system and additional agent integrations. Community activity included blog posts, tutorials, and meetup sessions. A category summary is in the table below, followed by details.
|
||||||
|
|
||||||
|
| **Spec Kit Core (Feb 2026)** | **Community & Content** | **Roadmap & Next** |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Versions **v0.1.7** through **v0.1.13** shipped with bug fixes and features, including a **dual-catalog extension system** and new agent integrations. Over 300 issues were closed (of ~800 filed). The repo reached 71k stars and 6.4k forks. [\[github.com\]](https://github.com/github/spec-kit/releases) [\[github.com\]](https://github.com/github/spec-kit/issues) [\[rywalker.com\]](https://rywalker.com/research/github-spec-kit) | Eduardo Luz published a LinkedIn article on SDD and Spec Kit [\[linkedin.com\]](https://www.linkedin.com/pulse/specification-driven-development-sdd-github-spec-kit-elevating-luz-tojmc?tl=en). Erick Matsen blogged a walkthrough of building a bioinformatics pipeline with Spec Kit [\[matsen.fredhutch.org\]](https://matsen.fredhutch.org/general/2026/02/10/spec-kit-walkthrough.html). Microsoft MVP [Eric Boyd](https://ericboyd.com/) (not the Microsoft AI Platform VP of the same name) presented at the Cleveland .NET User Group [\[ericboyd.com\]](https://ericboyd.com/events/cleveland-csharp-user-group-february-25-2026-spec-driven-development-sdd-github-spec-kit). | **v0.2.0** was released in early March, consolidating February's work. It added extensions for Jira and Azure DevOps, community plugin support, and agents for Tabnine CLI and Kiro CLI [\[github.com\]](https://github.com/github/spec-kit/releases). Future work includes spec lifecycle management and progress toward a stable 1.0 release [\[martinfowler.com\]](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html). |
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
## Spec Kit Project Updates
|
||||||
|
|
||||||
|
Spec Kit released versions **v0.1.7** through **v0.1.13** during February. Version 0.1.7 (early February) updated documentation for the newly introduced **dual-catalog extension system**, which allows both core and community extension catalogs to coexist. Subsequent patches (0.1.8, 0.1.9, etc.) bumped dependencies such as GitHub Actions versions and resolved minor issues. **v0.1.10** fixed YAML front-matter handling in generated files. By late February, **v0.1.12** and **v0.1.13** shipped with additional fixes in preparation for the next version bump. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||||
|
|
||||||
|
The main architectural addition was the **modular extension system** with separate "core" and "community" extension catalogs for third-party add-ons. Multiple community-contributed extensions were merged during the month, including a **Jira extension** for issue tracker integration, an **Azure DevOps extension**, and utility extensions for code review, retrospective documentation, and CI/CD sync. The pending 0.2.0 release changelog lists over a dozen changes from February, including the extension additions and support for **multiple agent catalogs concurrently**. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||||
|
|
||||||
|
By end of February, **over 330 issues/feature requests had been closed on GitHub** (out of ~870 filed to date). External contributors submitted pull requests including the **Tabnine CLI support**, which was merged in late February. The repository reached ~71k stars and crossed 6,000 forks. [\[github.com\]](https://github.com/github/spec-kit/issues) [\[github.com\]](https://github.com/github/spec-kit/releases) [\[rywalker.com\]](https://rywalker.com/research/github-spec-kit)
|
||||||
|
|
||||||
|
On the stability side, February's work focused on tightening core workflows and fixing edge-case bugs in the specification, planning, and task-generation commands. The team addressed file-handling issues (e.g., clarifying how output files are created/appended) and improved the reliability of the automated release pipeline. The project also added **Kiro CLI** to the supported agent list and updated integration scripts for Cursor and Code Interpreter, bringing the total number of supported AI coding assistants to over 20. [\[github.com\]](https://github.com/github/spec-kit/releases) [\[github.com\]](https://github.com/github/spec-kit)
|
||||||
|
|
||||||
|
## Community & Content
|
||||||
|
|
||||||
|
**Eduardo Luz** published a LinkedIn article on Feb 15 titled *"Specification Driven Development (SDD) and the GitHub Spec Kit: Elevating Software Engineering."* The article draws on his experience as a senior engineer to describe common causes of technical debt and inconsistent designs, and how SDD addresses them. It walks through Spec Kit's **four-layer approach** (Constitution, Design, Tasks, Implementation) and discusses treating specifications as a source of truth. The post generated discussion among software architects on LinkedIn about reducing misunderstandings and rework through spec-driven workflows. [\[linkedin.com\]](https://www.linkedin.com/pulse/specification-driven-development-sdd-github-spec-kit-elevating-luz-tojmc?tl=en)
|
||||||
|
|
||||||
|
**Erick Matsen** (Fred Hutchinson Cancer Center) posted a detailed walkthrough on Feb 10 titled *"Spec-Driven Development with spec-kit."* He describes building a **bioinformatics pipeline** in a single day using Spec Kit's workflow (from `speckit.constitution` to `speckit.implement`). The post includes command outputs and notes on decisions made along the way, such as refining the spec to add domain-specific requirements. He writes: "I really recommend this approach. This feels like the way software development should be." [\[matsen.fredhutch.org\]](https://matsen.fredhutch.org/general/2026/02/10/spec-kit-walkthrough.html) [\[github.com\]](https://github.com/mnriem/spec-kit-dotnet-cli-demo)
|
||||||
|
|
||||||
|
Several other tutorials and guides appeared during the month. An article on *IntuitionLabs* (updated Feb 21) provided a guide to Spec Kit covering the philosophy behind SDD and a walkthrough of the four-phase workflow with examples. A piece by Ry Walker (Feb 22) summarized key aspects of Spec Kit, noting its agent-agnostic design and 71k-star count. Microsoft's Developer Blog post from late 2025 (*"Diving Into Spec-Driven Development with GitHub Spec Kit"* by Den Delimarsky) continued to circulate among new users. [\[intuitionlabs.ai\]](https://intuitionlabs.ai/articles/spec-driven-development-spec-kit) [\[rywalker.com\]](https://rywalker.com/research/github-spec-kit)
|
||||||
|
|
||||||
|
On **Feb 25**, the Cleveland C# .NET User Group hosted a session titled *"Spec Driven Development with GitHub Spec Kit."* The talk was delivered by Microsoft MVP **[Eric Boyd](https://ericboyd.com/)** (Cleveland-based .NET developer; not to be confused with the Microsoft AI Platform VP of the same name). Boyd covered how specs change an AI coding assistant's output, patterns for iterating and refining specs over multiple cycles, and moving from ad-hoc prompting to a repeatable spec-driven workflow. Other groups, including GDG Madison, also listed sessions on spec-driven development in late February and early March. [\[ericboyd.com\]](https://ericboyd.com/events/cleveland-csharp-user-group-february-25-2026-spec-driven-development-sdd-github-spec-kit)
|
||||||
|
|
||||||
|
On GitHub, the **Spec Kit Discussions forum** saw activity around installation troubleshooting, handling multi-feature projects with Spec Kit's branching model, and feature suggestions. One thread discussed how Spec Kit treats each spec as a short-lived artifact tied to a feature branch, which led to discussion about future support for long-running "spec of record" use cases. [\[martinfowler.com\]](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html)
|
||||||
|
|
||||||
|
## SDD Ecosystem
|
||||||
|
|
||||||
|
Other spec-driven development tools also saw activity in February.
|
||||||
|
|
||||||
|
AWS **Kiro** released version 0.10 on Feb 18 with two new spec workflows: a **Design-First** mode (starting from architecture/pseudocode to derive requirements) and a **Bugfix** mode (structured root-cause analysis producing a `bugfix.md` spec file). Kiro also added hunk-level code review for AI-generated changes and pre/post task hooks for custom automation. AWS expanded Kiro to GovCloud regions on Feb 17 for government compliance use cases. [\[kiro.dev\]](https://kiro.dev/changelog/)
|
||||||
|
|
||||||
|
**OpenSpec** (by Fission AI), a lightweight SDD framework, reached ~29.3k stars and nearly 2k forks. Its community published guides and comparisons during the month, including *"Spec-Driven Development Made Easy: A Practical Guide with OpenSpec."* OpenSpec emphasizes simplicity and flexibility, integrating with multiple AI coding assistants via YAML configs.
|
||||||
|
|
||||||
|
**Tessl** remained in private beta. As described by Thoughtworks writer Birgitta Boeckeler, Tessl pursues a **spec-as-source** model where specifications are maintained long-term and directly generate code files one-to-one, with generated code labeled as "do not edit." This contrasts with Spec Kit's current approach of creating specs per feature/branch. [\[martinfowler.com\]](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html)
|
||||||
|
|
||||||
|
An **arXiv preprint** (January 2026) categorized SDD implementations into three levels: *spec-first*, *spec-anchored*, and *spec-as-source*. Spec Kit was identified as primarily spec-first with elements of spec-anchored. Tech media published reviews including a *Vibe Coding* "GitHub Spec Kit Review (2026)" and a blog post titled *"Putting Spec Kit Through Its Paces: Radical Idea or Reinvented Waterfall?"* which concluded that SDD with AI assistance is more iterative than traditional Waterfall. [\[intuitionlabs.ai\]](https://intuitionlabs.ai/articles/spec-driven-development-spec-kit) [\[martinfowler.com\]](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html)
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
**v0.2.0** was released on March 10, 2026, consolidating the month's work. It includes new extensions (Jira, Azure DevOps, review, sync), support for multiple extension catalogs and community plugins, and additional agent integrations (Tabnine CLI, Kiro CLI). [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||||
|
|
||||||
|
Areas under discussion or in progress for future development:
|
||||||
|
|
||||||
|
- **Spec lifecycle management** -- supporting longer-lived specifications that can evolve across multiple iterations, rather than being tied to a single feature branch. Users have raised this in GitHub Discussions, and the concept of "spec-anchored" development is under consideration. [\[martinfowler.com\]](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html)
|
||||||
|
- **CI/CD integration** -- incorporating Spec Kit verification (e.g., `speckit.checklist` or `speckit.verify`) into pull request workflows and project management tools. February's Jira and Azure DevOps extensions are a step in this direction. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||||
|
- **Continued agent support** -- adding integrations as new AI coding assistants emerge. The project currently supports over 20 agents and has been adding new ones (Kiro CLI, Tabnine CLI) as they become available. [\[github.com\]](https://github.com/github/spec-kit)
|
||||||
|
- **Community ecosystem** -- the open extension model allows external contributors to add functionality directly. February's Jira and Azure DevOps plugins were community-contributed. The Spec Kit README now links to community walkthrough demos for .NET, Spring Boot, and other stacks. [\[github.com\]](https://github.com/github/spec-kit)
|
||||||
157
presets/ARCHITECTURE.md
Normal file
157
presets/ARCHITECTURE.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Preset System Architecture
|
||||||
|
|
||||||
|
This document describes the internal architecture of the preset system — how template resolution, command registration, and catalog management work under the hood.
|
||||||
|
|
||||||
|
For usage instructions, see [README.md](README.md).
|
||||||
|
|
||||||
|
## Template Resolution
|
||||||
|
|
||||||
|
When Spec Kit needs a template (e.g. `spec-template`), the `PresetResolver` walks a priority stack and returns the first match:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["resolve_template('spec-template')"] --> B{Override exists?}
|
||||||
|
B -- Yes --> C[".specify/templates/overrides/spec-template.md"]
|
||||||
|
B -- No --> D{Preset provides it?}
|
||||||
|
D -- Yes --> E[".specify/presets/‹preset-id›/templates/spec-template.md"]
|
||||||
|
D -- No --> F{Extension provides it?}
|
||||||
|
F -- Yes --> G[".specify/extensions/‹ext-id›/templates/spec-template.md"]
|
||||||
|
F -- No --> H[".specify/templates/spec-template.md"]
|
||||||
|
|
||||||
|
E -- "multiple presets?" --> I["lowest priority number wins"]
|
||||||
|
I --> E
|
||||||
|
|
||||||
|
style C fill:#4caf50,color:#fff
|
||||||
|
style E fill:#2196f3,color:#fff
|
||||||
|
style G fill:#ff9800,color:#fff
|
||||||
|
style H fill:#9e9e9e,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
| Priority | Source | Path | Use case |
|
||||||
|
|----------|--------|------|----------|
|
||||||
|
| 1 (highest) | Override | `.specify/templates/overrides/` | One-off project-local tweaks |
|
||||||
|
| 2 | Preset | `.specify/presets/<id>/templates/` | Shareable, stackable customizations |
|
||||||
|
| 3 | Extension | `.specify/extensions/<id>/templates/` | Extension-provided templates |
|
||||||
|
| 4 (lowest) | Core | `.specify/templates/` | Shipped defaults |
|
||||||
|
|
||||||
|
When multiple presets are installed, they're sorted by their `priority` field (lower number = higher precedence). This is set via `--priority` on `specify preset add`.
|
||||||
|
|
||||||
|
The resolution is implemented three times to ensure consistency:
|
||||||
|
- **Python**: `PresetResolver` in `src/specify_cli/presets.py`
|
||||||
|
- **Bash**: `resolve_template()` in `scripts/bash/common.sh`
|
||||||
|
- **PowerShell**: `Resolve-Template` in `scripts/powershell/common.ps1`
|
||||||
|
|
||||||
|
## Command Registration
|
||||||
|
|
||||||
|
When a preset is installed with `type: "command"` entries, the `PresetManager` registers them into all detected agent directories using the shared `CommandRegistrar` from `src/specify_cli/agents.py`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["specify preset add my-preset"] --> B{Preset has type: command?}
|
||||||
|
B -- No --> Z["done (templates only)"]
|
||||||
|
B -- Yes --> C{Extension command?}
|
||||||
|
C -- "speckit.myext.cmd\n(3+ dot segments)" --> D{Extension installed?}
|
||||||
|
D -- No --> E["skip (extension not active)"]
|
||||||
|
D -- Yes --> F["register command"]
|
||||||
|
C -- "speckit.specify\n(core command)" --> F
|
||||||
|
F --> G["detect agent directories"]
|
||||||
|
G --> H[".claude/commands/"]
|
||||||
|
G --> I[".gemini/commands/"]
|
||||||
|
G --> J[".github/agents/"]
|
||||||
|
G --> K["... (17+ agents)"]
|
||||||
|
H --> L["write .md (Markdown format)"]
|
||||||
|
I --> M["write .toml (TOML format)"]
|
||||||
|
J --> N["write .agent.md + .prompt.md"]
|
||||||
|
|
||||||
|
style E fill:#ff5722,color:#fff
|
||||||
|
style L fill:#4caf50,color:#fff
|
||||||
|
style M fill:#4caf50,color:#fff
|
||||||
|
style N fill:#4caf50,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extension safety check
|
||||||
|
|
||||||
|
Command names follow the pattern `speckit.<ext-id>.<cmd-name>`. When a command has 3+ dot segments, the system extracts the extension ID and checks if `.specify/extensions/<ext-id>/` exists. If the extension isn't installed, the command is skipped — preventing orphan files referencing non-existent extensions.
|
||||||
|
|
||||||
|
Core commands (e.g. `speckit.specify`, with only 2 segments) are always registered.
|
||||||
|
|
||||||
|
### Agent format rendering
|
||||||
|
|
||||||
|
The `CommandRegistrar` renders commands differently per agent:
|
||||||
|
|
||||||
|
| Agent | Format | Extension | Arg placeholder |
|
||||||
|
|-------|--------|-----------|-----------------|
|
||||||
|
| Claude, Cursor, opencode, Windsurf, etc. | Markdown | `.md` | `$ARGUMENTS` |
|
||||||
|
| Copilot | Markdown | `.agent.md` + `.prompt.md` | `$ARGUMENTS` |
|
||||||
|
| Gemini, Qwen, Tabnine | TOML | `.toml` | `{{args}}` |
|
||||||
|
|
||||||
|
### Cleanup on removal
|
||||||
|
|
||||||
|
When `specify preset remove` is called, the registered commands are read from the registry metadata and the corresponding files are deleted from each agent directory, including Copilot companion `.prompt.md` files.
|
||||||
|
|
||||||
|
## Catalog System
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A["specify preset search"] --> B["PresetCatalog.get_active_catalogs()"]
|
||||||
|
B --> C{SPECKIT_PRESET_CATALOG_URL set?}
|
||||||
|
C -- Yes --> D["single custom catalog"]
|
||||||
|
C -- No --> E{.specify/preset-catalogs.yml exists?}
|
||||||
|
E -- Yes --> F["project-level catalog stack"]
|
||||||
|
E -- No --> G{"~/.specify/preset-catalogs.yml exists?"}
|
||||||
|
G -- Yes --> H["user-level catalog stack"]
|
||||||
|
G -- No --> I["built-in defaults"]
|
||||||
|
I --> J["default (install allowed)"]
|
||||||
|
I --> K["community (discovery only)"]
|
||||||
|
|
||||||
|
style D fill:#ff9800,color:#fff
|
||||||
|
style F fill:#2196f3,color:#fff
|
||||||
|
style H fill:#2196f3,color:#fff
|
||||||
|
style J fill:#4caf50,color:#fff
|
||||||
|
style K fill:#9e9e9e,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
Catalogs are fetched with a 1-hour cache (per-URL, SHA256-hashed cache files). Each catalog entry has a `priority` (for merge ordering) and `install_allowed` flag.
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
presets/
|
||||||
|
├── ARCHITECTURE.md # This file
|
||||||
|
├── PUBLISHING.md # Guide for submitting presets to the catalog
|
||||||
|
├── README.md # User guide
|
||||||
|
├── catalog.json # Official preset catalog
|
||||||
|
├── catalog.community.json # Community preset catalog
|
||||||
|
├── scaffold/ # Scaffold for creating new presets
|
||||||
|
│ ├── preset.yml # Example manifest
|
||||||
|
│ ├── README.md # Guide for customizing the scaffold
|
||||||
|
│ ├── commands/
|
||||||
|
│ │ ├── speckit.specify.md # Core command override example
|
||||||
|
│ │ └── speckit.myext.myextcmd.md # Extension command override example
|
||||||
|
│ └── templates/
|
||||||
|
│ ├── spec-template.md # Core template override example
|
||||||
|
│ └── myext-template.md # Extension template override example
|
||||||
|
└── self-test/ # Self-test preset (overrides all core templates)
|
||||||
|
├── preset.yml
|
||||||
|
├── commands/
|
||||||
|
│ └── speckit.specify.md
|
||||||
|
└── templates/
|
||||||
|
├── spec-template.md
|
||||||
|
├── plan-template.md
|
||||||
|
├── tasks-template.md
|
||||||
|
├── checklist-template.md
|
||||||
|
├── constitution-template.md
|
||||||
|
└── agent-file-template.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Module Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/specify_cli/
|
||||||
|
├── agents.py # CommandRegistrar — shared infrastructure for writing
|
||||||
|
│ # command files to agent directories
|
||||||
|
├── presets.py # PresetManifest, PresetRegistry, PresetManager,
|
||||||
|
│ # PresetCatalog, PresetCatalogEntry, PresetResolver
|
||||||
|
└── __init__.py # CLI commands: specify preset list/add/remove/search/
|
||||||
|
# resolve/info, specify preset catalog list/add/remove
|
||||||
|
```
|
||||||
295
presets/PUBLISHING.md
Normal file
295
presets/PUBLISHING.md
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
# Preset Publishing Guide
|
||||||
|
|
||||||
|
This guide explains how to publish your preset to the Spec Kit preset catalog, making it discoverable by `specify preset search`.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Prerequisites](#prerequisites)
|
||||||
|
2. [Prepare Your Preset](#prepare-your-preset)
|
||||||
|
3. [Submit to Catalog](#submit-to-catalog)
|
||||||
|
4. [Verification Process](#verification-process)
|
||||||
|
5. [Release Workflow](#release-workflow)
|
||||||
|
6. [Best Practices](#best-practices)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before publishing a preset, ensure you have:
|
||||||
|
|
||||||
|
1. **Valid Preset**: A working preset with a valid `preset.yml` manifest
|
||||||
|
2. **Git Repository**: Preset hosted on GitHub (or other public git hosting)
|
||||||
|
3. **Documentation**: README.md with description and usage instructions
|
||||||
|
4. **License**: Open source license file (MIT, Apache 2.0, etc.)
|
||||||
|
5. **Versioning**: Semantic versioning (e.g., 1.0.0)
|
||||||
|
6. **Testing**: Preset tested on real projects with `specify preset add --dev`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prepare Your Preset
|
||||||
|
|
||||||
|
### 1. Preset Structure
|
||||||
|
|
||||||
|
Ensure your preset follows the standard structure:
|
||||||
|
|
||||||
|
```text
|
||||||
|
your-preset/
|
||||||
|
├── preset.yml # Required: Preset manifest
|
||||||
|
├── README.md # Required: Documentation
|
||||||
|
├── LICENSE # Required: License file
|
||||||
|
├── CHANGELOG.md # Recommended: Version history
|
||||||
|
│
|
||||||
|
├── templates/ # Template overrides
|
||||||
|
│ ├── spec-template.md
|
||||||
|
│ ├── plan-template.md
|
||||||
|
│ └── ...
|
||||||
|
│
|
||||||
|
└── commands/ # Command overrides (optional)
|
||||||
|
└── speckit.specify.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Start from the [scaffold](scaffold/) if you're creating a new preset.
|
||||||
|
|
||||||
|
### 2. preset.yml Validation
|
||||||
|
|
||||||
|
Verify your manifest is valid:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
preset:
|
||||||
|
id: "your-preset" # Unique lowercase-hyphenated ID
|
||||||
|
name: "Your Preset Name" # Human-readable name
|
||||||
|
version: "1.0.0" # Semantic version
|
||||||
|
description: "Brief description (one sentence)"
|
||||||
|
author: "Your Name or Organization"
|
||||||
|
repository: "https://github.com/your-org/spec-kit-preset-your-preset"
|
||||||
|
license: "MIT"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.1.0" # Required spec-kit version
|
||||||
|
|
||||||
|
provides:
|
||||||
|
templates:
|
||||||
|
- type: "template"
|
||||||
|
name: "spec-template"
|
||||||
|
file: "templates/spec-template.md"
|
||||||
|
description: "Custom spec template"
|
||||||
|
replaces: "spec-template"
|
||||||
|
|
||||||
|
tags: # 2-5 relevant tags
|
||||||
|
- "category"
|
||||||
|
- "workflow"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation Checklist**:
|
||||||
|
|
||||||
|
- ✅ `id` is lowercase with hyphens only (no underscores, spaces, or special characters)
|
||||||
|
- ✅ `version` follows semantic versioning (X.Y.Z)
|
||||||
|
- ✅ `description` is concise (under 200 characters)
|
||||||
|
- ✅ `repository` URL is valid and public
|
||||||
|
- ✅ All template and command files exist in the preset directory
|
||||||
|
- ✅ Template names are lowercase with hyphens only
|
||||||
|
- ✅ Command names use dot notation (e.g. `speckit.specify`)
|
||||||
|
- ✅ Tags are lowercase and descriptive
|
||||||
|
|
||||||
|
### 3. Test Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install from local directory
|
||||||
|
specify preset add --dev /path/to/your-preset
|
||||||
|
|
||||||
|
# Verify templates resolve from your preset
|
||||||
|
specify preset resolve spec-template
|
||||||
|
|
||||||
|
# Verify preset info
|
||||||
|
specify preset info your-preset
|
||||||
|
|
||||||
|
# List installed presets
|
||||||
|
specify preset list
|
||||||
|
|
||||||
|
# Remove when done testing
|
||||||
|
specify preset remove your-preset
|
||||||
|
```
|
||||||
|
|
||||||
|
If your preset includes command overrides, verify they appear in the agent directories:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Claude commands (if using Claude)
|
||||||
|
ls .claude/commands/speckit.*.md
|
||||||
|
|
||||||
|
# Check Copilot commands (if using Copilot)
|
||||||
|
ls .github/agents/speckit.*.agent.md
|
||||||
|
|
||||||
|
# Check Gemini commands (if using Gemini)
|
||||||
|
ls .gemini/commands/speckit.*.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Create GitHub Release
|
||||||
|
|
||||||
|
Create a GitHub release for your preset version:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tag the release
|
||||||
|
git tag v1.0.0
|
||||||
|
git push origin v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
The release archive URL will be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Test Installation from Archive
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify preset add --from https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Submit to Catalog
|
||||||
|
|
||||||
|
### Understanding the Catalogs
|
||||||
|
|
||||||
|
Spec Kit uses a dual-catalog system:
|
||||||
|
|
||||||
|
- **`catalog.json`** — Official, verified presets (install allowed by default)
|
||||||
|
- **`catalog.community.json`** — Community-contributed presets (discovery only by default)
|
||||||
|
|
||||||
|
All community presets should be submitted to `catalog.community.json`.
|
||||||
|
|
||||||
|
### 1. Fork the spec-kit Repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/YOUR-USERNAME/spec-kit.git
|
||||||
|
cd spec-kit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add Preset to Community Catalog
|
||||||
|
|
||||||
|
Edit `presets/catalog.community.json` and add your preset.
|
||||||
|
|
||||||
|
> **⚠️ Entries must be sorted alphabetically by preset ID.** Insert your preset in the correct position within the `"presets"` object.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-03-10T00:00:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.community.json",
|
||||||
|
"presets": {
|
||||||
|
"your-preset": {
|
||||||
|
"name": "Your Preset Name",
|
||||||
|
"description": "Brief description of what your preset provides",
|
||||||
|
"author": "Your Name",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"download_url": "https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip",
|
||||||
|
"repository": "https://github.com/your-org/spec-kit-preset-your-preset",
|
||||||
|
"license": "MIT",
|
||||||
|
"requires": {
|
||||||
|
"speckit_version": ">=0.1.0"
|
||||||
|
},
|
||||||
|
"provides": {
|
||||||
|
"templates": 3,
|
||||||
|
"commands": 1
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"category",
|
||||||
|
"workflow"
|
||||||
|
],
|
||||||
|
"created_at": "2026-03-10T00:00:00Z",
|
||||||
|
"updated_at": "2026-03-10T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Submit Pull Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b add-your-preset
|
||||||
|
git add presets/catalog.community.json
|
||||||
|
git commit -m "Add your-preset to community catalog
|
||||||
|
|
||||||
|
- Preset ID: your-preset
|
||||||
|
- Version: 1.0.0
|
||||||
|
- Author: Your Name
|
||||||
|
- Description: Brief description
|
||||||
|
"
|
||||||
|
git push origin add-your-preset
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pull Request Checklist**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Preset Submission
|
||||||
|
|
||||||
|
**Preset Name**: Your Preset Name
|
||||||
|
**Preset ID**: your-preset
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Repository**: https://github.com/your-org/spec-kit-preset-your-preset
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
- [ ] Valid preset.yml manifest
|
||||||
|
- [ ] README.md with description and usage
|
||||||
|
- [ ] LICENSE file included
|
||||||
|
- [ ] GitHub release created
|
||||||
|
- [ ] Preset tested with `specify preset add --dev`
|
||||||
|
- [ ] Templates resolve correctly (`specify preset resolve`)
|
||||||
|
- [ ] Commands register to agent directories (if applicable)
|
||||||
|
- [ ] Commands match template sections (command + template are coherent)
|
||||||
|
- [ ] Added to presets/catalog.community.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Process
|
||||||
|
|
||||||
|
After submission, maintainers will review:
|
||||||
|
|
||||||
|
1. **Manifest validation** — valid `preset.yml`, all files exist
|
||||||
|
2. **Template quality** — templates are useful and well-structured
|
||||||
|
3. **Command coherence** — commands reference sections that exist in templates
|
||||||
|
4. **Security** — no malicious content, safe file operations
|
||||||
|
5. **Documentation** — clear README explaining what the preset does
|
||||||
|
|
||||||
|
Once verified, `verified: true` is set and the preset appears in `specify preset search`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Release Workflow
|
||||||
|
|
||||||
|
When releasing a new version:
|
||||||
|
|
||||||
|
1. Update `version` in `preset.yml`
|
||||||
|
2. Update CHANGELOG.md
|
||||||
|
3. Tag and push: `git tag v1.1.0 && git push origin v1.1.0`
|
||||||
|
4. Submit PR to update `version` and `download_url` in `presets/catalog.community.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Template Design
|
||||||
|
|
||||||
|
- **Keep sections clear** — use headings and placeholder text the LLM can replace
|
||||||
|
- **Match commands to templates** — if your preset overrides a command, make sure it references the sections in your template
|
||||||
|
- **Document customization points** — use HTML comments to guide users on what to change
|
||||||
|
|
||||||
|
### Naming
|
||||||
|
|
||||||
|
- Preset IDs should be descriptive: `healthcare-compliance`, `enterprise-safe`, `startup-lean`
|
||||||
|
- Avoid generic names: `my-preset`, `custom`, `test`
|
||||||
|
|
||||||
|
### Stacking
|
||||||
|
|
||||||
|
- Design presets to work well when stacked with others
|
||||||
|
- Only override templates you need to change
|
||||||
|
- Document which templates and commands your preset modifies
|
||||||
|
|
||||||
|
### Command Overrides
|
||||||
|
|
||||||
|
- Only override commands when the workflow needs to change, not just the output format
|
||||||
|
- If you only need different template sections, a template override is sufficient
|
||||||
|
- Test command overrides with multiple agents (Claude, Gemini, Copilot)
|
||||||
115
presets/README.md
Normal file
115
presets/README.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Presets
|
||||||
|
|
||||||
|
Presets are stackable, priority-ordered collections of template and command overrides for Spec Kit. They let you customize both the artifacts produced by the Spec-Driven Development workflow (specs, plans, tasks, checklists, constitutions) and the commands that guide the LLM in creating them — without forking or modifying core files.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
When Spec Kit needs a template (e.g. `spec-template`), it walks a resolution stack:
|
||||||
|
|
||||||
|
1. `.specify/templates/overrides/` — project-local one-off overrides
|
||||||
|
2. `.specify/presets/<preset-id>/templates/` — installed presets (sorted by priority)
|
||||||
|
3. `.specify/extensions/<ext-id>/templates/` — extension-provided templates
|
||||||
|
4. `.specify/templates/` — core templates shipped with Spec Kit
|
||||||
|
|
||||||
|
If no preset is installed, core templates are used — exactly the same behavior as before presets existed.
|
||||||
|
|
||||||
|
For detailed resolution and command registration flows, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||||
|
|
||||||
|
## Command Overrides
|
||||||
|
|
||||||
|
Presets can also override the commands that guide the SDD workflow. Templates define *what* gets produced (specs, plans, constitutions); commands define *how* the LLM produces them (the step-by-step instructions).
|
||||||
|
|
||||||
|
When a preset includes `type: "command"` entries, the commands are automatically registered into all detected agent directories (`.claude/commands/`, `.gemini/commands/`, etc.) in the correct format (Markdown or TOML with appropriate argument placeholders). When the preset is removed, the registered commands are cleaned up.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Search available presets
|
||||||
|
specify preset search
|
||||||
|
|
||||||
|
# Install a preset from the catalog
|
||||||
|
specify preset add healthcare-compliance
|
||||||
|
|
||||||
|
# Install from a local directory (for development)
|
||||||
|
specify preset add --dev ./my-preset
|
||||||
|
|
||||||
|
# Install with a specific priority (lower = higher precedence)
|
||||||
|
specify preset add healthcare-compliance --priority 5
|
||||||
|
|
||||||
|
# List installed presets
|
||||||
|
specify preset list
|
||||||
|
|
||||||
|
# See which template a name resolves to
|
||||||
|
specify preset resolve spec-template
|
||||||
|
|
||||||
|
# Get detailed info about a preset
|
||||||
|
specify preset info healthcare-compliance
|
||||||
|
|
||||||
|
# Remove a preset
|
||||||
|
specify preset remove healthcare-compliance
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stacking Presets
|
||||||
|
|
||||||
|
Multiple presets can be installed simultaneously. The `--priority` flag controls which one wins when two presets provide the same template (lower number = higher precedence):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify preset add enterprise-safe --priority 10 # base layer
|
||||||
|
specify preset add healthcare-compliance --priority 5 # overrides enterprise-safe
|
||||||
|
specify preset add pm-workflow --priority 1 # overrides everything
|
||||||
|
```
|
||||||
|
|
||||||
|
Presets **override**, they don't merge. If two presets both provide `spec-template`, the one with the lowest priority number wins entirely.
|
||||||
|
|
||||||
|
## Catalog Management
|
||||||
|
|
||||||
|
Presets are discovered through catalogs. By default, Spec Kit uses the official and community catalogs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List active catalogs
|
||||||
|
specify preset catalog list
|
||||||
|
|
||||||
|
# Add a custom catalog
|
||||||
|
specify preset catalog add https://example.com/catalog.json --name my-org --install-allowed
|
||||||
|
|
||||||
|
# Remove a catalog
|
||||||
|
specify preset catalog remove my-org
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a Preset
|
||||||
|
|
||||||
|
See [scaffold/](scaffold/) for a scaffold you can copy to create your own preset.
|
||||||
|
|
||||||
|
1. Copy `scaffold/` to a new directory
|
||||||
|
2. Edit `preset.yml` with your preset's metadata
|
||||||
|
3. Add or replace templates in `templates/`
|
||||||
|
4. Test locally with `specify preset add --dev .`
|
||||||
|
5. Verify with `specify preset resolve spec-template`
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `SPECKIT_PRESET_CATALOG_URL` | Override the catalog URL (replaces all defaults) |
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
| File | Scope | Description |
|
||||||
|
|------|-------|-------------|
|
||||||
|
| `.specify/preset-catalogs.yml` | Project | Custom catalog stack for this project |
|
||||||
|
| `~/.specify/preset-catalogs.yml` | User | Custom catalog stack for all projects |
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
The following enhancements are under consideration for future releases:
|
||||||
|
|
||||||
|
- **Composition strategies** — Allow presets to declare a `strategy` per template instead of the default `replace`:
|
||||||
|
|
||||||
|
| Type | `replace` | `prepend` | `append` | `wrap` |
|
||||||
|
|------|-----------|-----------|----------|--------|
|
||||||
|
| **template** | ✓ (default) | ✓ | ✓ | ✓ |
|
||||||
|
| **command** | ✓ (default) | ✓ | ✓ | ✓ |
|
||||||
|
| **script** | ✓ (default) | — | — | ✓ |
|
||||||
|
|
||||||
|
For artifacts and commands (which are LLM directives), `wrap` would inject preset content before and after the core template using a `{CORE_TEMPLATE}` placeholder. For scripts, `wrap` would run custom logic before/after the core script via a `$CORE_SCRIPT` variable.
|
||||||
|
- **Script overrides** — Enable presets to provide alternative versions of core scripts (e.g. `create-new-feature.sh`) for workflow customization. A `strategy: "wrap"` option could allow presets to run custom logic before/after the core script without fully replacing it.
|
||||||
6
presets/catalog.community.json
Normal file
6
presets/catalog.community.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-03-09T00:00:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.community.json",
|
||||||
|
"presets": {}
|
||||||
|
}
|
||||||
6
presets/catalog.json
Normal file
6
presets/catalog.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"updated_at": "2026-03-10T00:00:00Z",
|
||||||
|
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.json",
|
||||||
|
"presets": {}
|
||||||
|
}
|
||||||
46
presets/scaffold/README.md
Normal file
46
presets/scaffold/README.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# My Preset
|
||||||
|
|
||||||
|
A custom preset for Spec Kit. Copy this directory and customize it to create your own.
|
||||||
|
|
||||||
|
## Templates Included
|
||||||
|
|
||||||
|
| Template | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `spec-template` | template | Custom feature specification template (overrides core and extensions) |
|
||||||
|
| `myext-template` | template | Override of the myext extension's report template |
|
||||||
|
| `speckit.specify` | command | Custom specification command (overrides core) |
|
||||||
|
| `speckit.myext.myextcmd` | command | Override of the myext extension's myextcmd command |
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
1. Copy this directory: `cp -r presets/scaffold my-preset`
|
||||||
|
2. Edit `preset.yml` — set your preset's ID, name, description, and templates
|
||||||
|
3. Add or modify templates in `templates/`
|
||||||
|
4. Test locally: `specify preset add --dev ./my-preset`
|
||||||
|
5. Verify resolution: `specify preset resolve spec-template`
|
||||||
|
6. Remove when done testing: `specify preset remove my-preset`
|
||||||
|
|
||||||
|
## Manifest Reference (`preset.yml`)
|
||||||
|
|
||||||
|
Required fields:
|
||||||
|
- `schema_version` — always `"1.0"`
|
||||||
|
- `preset.id` — lowercase alphanumeric with hyphens
|
||||||
|
- `preset.name` — human-readable name
|
||||||
|
- `preset.version` — semantic version (e.g. `1.0.0`)
|
||||||
|
- `preset.description` — brief description
|
||||||
|
- `requires.speckit_version` — version constraint (e.g. `>=0.1.0`)
|
||||||
|
- `provides.templates` — list of templates with `type`, `name`, and `file`
|
||||||
|
|
||||||
|
## Template Types
|
||||||
|
|
||||||
|
- **template** — Document scaffolds (spec-template.md, plan-template.md, tasks-template.md, etc.)
|
||||||
|
- **command** — AI agent workflow prompts (e.g. speckit.specify, speckit.plan)
|
||||||
|
- **script** — Custom scripts (reserved for future use)
|
||||||
|
|
||||||
|
## Publishing
|
||||||
|
|
||||||
|
See the [Preset Publishing Guide](../PUBLISHING.md) for details on submitting to the catalog.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
20
presets/scaffold/commands/speckit.myext.myextcmd.md
Normal file
20
presets/scaffold/commands/speckit.myext.myextcmd.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
description: "Override of the myext extension's myextcmd command"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- Preset override for speckit.myext.myextcmd -->
|
||||||
|
|
||||||
|
You are following a customized version of the myext extension's myextcmd command.
|
||||||
|
|
||||||
|
When executing this command:
|
||||||
|
|
||||||
|
1. Read the user's input from $ARGUMENTS
|
||||||
|
2. Follow the standard myextcmd workflow
|
||||||
|
3. Additionally, apply the following customizations from this preset:
|
||||||
|
- Add compliance checks before proceeding
|
||||||
|
- Include audit trail entries in the output
|
||||||
|
|
||||||
|
> CUSTOMIZE: Replace the instructions above with your own.
|
||||||
|
> This file overrides the command that the "myext" extension provides.
|
||||||
|
> When this preset is installed, all agents (Claude, Gemini, Copilot, etc.)
|
||||||
|
> will use this version instead of the extension's original.
|
||||||
23
presets/scaffold/commands/speckit.specify.md
Normal file
23
presets/scaffold/commands/speckit.specify.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
description: "Create a feature specification (preset override)"
|
||||||
|
scripts:
|
||||||
|
sh: scripts/bash/create-new-feature.sh "{ARGS}"
|
||||||
|
ps: scripts/powershell/create-new-feature.ps1 "{ARGS}"
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ARGUMENTS
|
||||||
|
```
|
||||||
|
|
||||||
|
Given the feature description above:
|
||||||
|
|
||||||
|
1. **Create the feature branch** by running the script:
|
||||||
|
- Bash: `{SCRIPT} --json --short-name "<short-name>" "<description>"`
|
||||||
|
- The JSON output contains BRANCH_NAME and SPEC_FILE paths.
|
||||||
|
|
||||||
|
2. **Read the spec-template** to see the sections you need to fill.
|
||||||
|
|
||||||
|
3. **Write the specification** to SPEC_FILE, replacing the placeholders in each section
|
||||||
|
(Overview, Requirements, Acceptance Criteria) with details from the user's description.
|
||||||
91
presets/scaffold/preset.yml
Normal file
91
presets/scaffold/preset.yml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
preset:
|
||||||
|
# CUSTOMIZE: Change 'my-preset' to your preset ID (lowercase, hyphen-separated)
|
||||||
|
id: "my-preset"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Human-readable name for your preset
|
||||||
|
name: "My Preset"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Update version when releasing (semantic versioning: X.Y.Z)
|
||||||
|
version: "1.0.0"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Brief description (under 200 characters)
|
||||||
|
description: "Brief description of what your preset provides"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Your name or organization name
|
||||||
|
author: "Your Name"
|
||||||
|
|
||||||
|
# CUSTOMIZE: GitHub repository URL (create before publishing)
|
||||||
|
repository: "https://github.com/your-org/spec-kit-preset-my-preset"
|
||||||
|
|
||||||
|
# REVIEW: License (MIT is recommended for open source)
|
||||||
|
license: "MIT"
|
||||||
|
|
||||||
|
# Requirements for this preset
|
||||||
|
requires:
|
||||||
|
# CUSTOMIZE: Minimum spec-kit version required
|
||||||
|
speckit_version: ">=0.1.0"
|
||||||
|
|
||||||
|
# Templates provided by this preset
|
||||||
|
provides:
|
||||||
|
templates:
|
||||||
|
# CUSTOMIZE: Define your template overrides
|
||||||
|
# Templates are document scaffolds (spec-template.md, plan-template.md, etc.)
|
||||||
|
- type: "template"
|
||||||
|
name: "spec-template"
|
||||||
|
file: "templates/spec-template.md"
|
||||||
|
description: "Custom feature specification template"
|
||||||
|
replaces: "spec-template" # Which core template this overrides (optional)
|
||||||
|
|
||||||
|
# ADD MORE TEMPLATES: Copy this block for each template
|
||||||
|
# - type: "template"
|
||||||
|
# name: "plan-template"
|
||||||
|
# file: "templates/plan-template.md"
|
||||||
|
# description: "Custom plan template"
|
||||||
|
# replaces: "plan-template"
|
||||||
|
|
||||||
|
# OVERRIDE EXTENSION TEMPLATES:
|
||||||
|
# Presets sit above extensions in the resolution stack, so you can
|
||||||
|
# override templates provided by any installed extension.
|
||||||
|
# For example, if the "myext" extension provides a spec-template,
|
||||||
|
# the preset's version above will take priority automatically.
|
||||||
|
|
||||||
|
# Override a template provided by the "myext" extension:
|
||||||
|
- type: "template"
|
||||||
|
name: "myext-template"
|
||||||
|
file: "templates/myext-template.md"
|
||||||
|
description: "Override myext's report template"
|
||||||
|
replaces: "myext-template"
|
||||||
|
|
||||||
|
# Command overrides (AI agent workflow prompts)
|
||||||
|
# Presets can override both core and extension commands.
|
||||||
|
# Commands are automatically registered into all detected agent
|
||||||
|
# directories (.claude/commands/, .gemini/commands/, etc.)
|
||||||
|
|
||||||
|
# Override a core command:
|
||||||
|
- type: "command"
|
||||||
|
name: "speckit.specify"
|
||||||
|
file: "commands/speckit.specify.md"
|
||||||
|
description: "Custom specification command"
|
||||||
|
replaces: "speckit.specify"
|
||||||
|
|
||||||
|
# Override an extension command (e.g. from the "myext" extension):
|
||||||
|
- type: "command"
|
||||||
|
name: "speckit.myext.myextcmd"
|
||||||
|
file: "commands/speckit.myext.myextcmd.md"
|
||||||
|
description: "Override myext's myextcmd command with custom workflow"
|
||||||
|
replaces: "speckit.myext.myextcmd"
|
||||||
|
|
||||||
|
# Script templates (reserved for future use)
|
||||||
|
# - type: "script"
|
||||||
|
# name: "create-new-feature"
|
||||||
|
# file: "scripts/bash/create-new-feature.sh"
|
||||||
|
# description: "Custom feature creation script"
|
||||||
|
# replaces: "create-new-feature"
|
||||||
|
|
||||||
|
# CUSTOMIZE: Add relevant tags (2-5 recommended)
|
||||||
|
# Used for discovery in catalog
|
||||||
|
tags:
|
||||||
|
- "example"
|
||||||
|
- "preset"
|
||||||
24
presets/scaffold/templates/myext-template.md
Normal file
24
presets/scaffold/templates/myext-template.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# MyExt Report
|
||||||
|
|
||||||
|
> This template overrides the one provided by the "myext" extension.
|
||||||
|
> Customize it to match your needs.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Brief summary of the report.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
- Detail 1
|
||||||
|
- Detail 2
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
|
||||||
|
- [ ] Action 1
|
||||||
|
- [ ] Action 2
|
||||||
|
|
||||||
|
<!--
|
||||||
|
CUSTOMIZE: This template takes priority over the myext extension's
|
||||||
|
version of myext-template. The extension's original is still available
|
||||||
|
if you remove this preset.
|
||||||
|
-->
|
||||||
18
presets/scaffold/templates/spec-template.md
Normal file
18
presets/scaffold/templates/spec-template.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Feature Specification: [FEATURE NAME]
|
||||||
|
|
||||||
|
**Created**: [DATE]
|
||||||
|
**Status**: Draft
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
[Brief description of the feature]
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [ ] Requirement 1
|
||||||
|
- [ ] Requirement 2
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Criterion 1
|
||||||
|
- [ ] Criterion 2
|
||||||
15
presets/self-test/commands/speckit.specify.md
Normal file
15
presets/self-test/commands/speckit.specify.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
description: "Self-test override of the specify command"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
You are following the self-test preset's version of the specify command.
|
||||||
|
|
||||||
|
When creating a specification, follow this process:
|
||||||
|
|
||||||
|
1. Read the user's requirements from $ARGUMENTS
|
||||||
|
2. Create a specification document using the spec-template
|
||||||
|
3. Include all standard sections plus the self-test marker
|
||||||
|
|
||||||
|
> This command is provided by the self-test preset.
|
||||||
61
presets/self-test/preset.yml
Normal file
61
presets/self-test/preset.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
schema_version: "1.0"
|
||||||
|
|
||||||
|
preset:
|
||||||
|
id: "self-test"
|
||||||
|
name: "Self-Test Preset"
|
||||||
|
version: "1.0.0"
|
||||||
|
description: "A preset that overrides all core templates for testing purposes"
|
||||||
|
author: "github"
|
||||||
|
repository: "https://github.com/github/spec-kit"
|
||||||
|
license: "MIT"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
speckit_version: ">=0.1.0"
|
||||||
|
|
||||||
|
provides:
|
||||||
|
templates:
|
||||||
|
- type: "template"
|
||||||
|
name: "spec-template"
|
||||||
|
file: "templates/spec-template.md"
|
||||||
|
description: "Self-test spec template"
|
||||||
|
replaces: "spec-template"
|
||||||
|
|
||||||
|
- type: "template"
|
||||||
|
name: "plan-template"
|
||||||
|
file: "templates/plan-template.md"
|
||||||
|
description: "Self-test plan template"
|
||||||
|
replaces: "plan-template"
|
||||||
|
|
||||||
|
- type: "template"
|
||||||
|
name: "tasks-template"
|
||||||
|
file: "templates/tasks-template.md"
|
||||||
|
description: "Self-test tasks template"
|
||||||
|
replaces: "tasks-template"
|
||||||
|
|
||||||
|
- type: "template"
|
||||||
|
name: "checklist-template"
|
||||||
|
file: "templates/checklist-template.md"
|
||||||
|
description: "Self-test checklist template"
|
||||||
|
replaces: "checklist-template"
|
||||||
|
|
||||||
|
- type: "template"
|
||||||
|
name: "constitution-template"
|
||||||
|
file: "templates/constitution-template.md"
|
||||||
|
description: "Self-test constitution template"
|
||||||
|
replaces: "constitution-template"
|
||||||
|
|
||||||
|
- type: "template"
|
||||||
|
name: "agent-file-template"
|
||||||
|
file: "templates/agent-file-template.md"
|
||||||
|
description: "Self-test agent file template"
|
||||||
|
replaces: "agent-file-template"
|
||||||
|
|
||||||
|
- type: "command"
|
||||||
|
name: "speckit.specify"
|
||||||
|
file: "commands/speckit.specify.md"
|
||||||
|
description: "Self-test override of the specify command"
|
||||||
|
replaces: "speckit.specify"
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- "testing"
|
||||||
|
- "self-test"
|
||||||
9
presets/self-test/templates/agent-file-template.md
Normal file
9
presets/self-test/templates/agent-file-template.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Agent File (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Agent Instructions
|
||||||
|
|
||||||
|
Follow these guidelines when working on this project.
|
||||||
15
presets/self-test/templates/checklist-template.md
Normal file
15
presets/self-test/templates/checklist-template.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Checklist (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Pre-Implementation
|
||||||
|
|
||||||
|
- [ ] Spec reviewed
|
||||||
|
- [ ] Plan approved
|
||||||
|
|
||||||
|
## Post-Implementation
|
||||||
|
|
||||||
|
- [ ] Tests passing
|
||||||
|
- [ ] Documentation updated
|
||||||
15
presets/self-test/templates/constitution-template.md
Normal file
15
presets/self-test/templates/constitution-template.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Constitution (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Principles
|
||||||
|
|
||||||
|
1. Principle 1
|
||||||
|
2. Principle 2
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
- Guideline 1
|
||||||
|
- Guideline 2
|
||||||
22
presets/self-test/templates/plan-template.md
Normal file
22
presets/self-test/templates/plan-template.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Implementation Plan (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
|
||||||
|
Describe the implementation approach.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. Step 1
|
||||||
|
2. Step 2
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Dependency 1
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
- Risk 1
|
||||||
23
presets/self-test/templates/spec-template.md
Normal file
23
presets/self-test/templates/spec-template.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Feature Specification (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Brief description of the feature.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Requirement 1
|
||||||
|
- Requirement 2
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
Describe the design approach.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [ ] Criterion 1
|
||||||
|
- [ ] Criterion 2
|
||||||
17
presets/self-test/templates/tasks-template.md
Normal file
17
presets/self-test/templates/tasks-template.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Tasks (Self-Test Preset)
|
||||||
|
|
||||||
|
<!-- preset:self-test -->
|
||||||
|
|
||||||
|
> This template is provided by the self-test preset.
|
||||||
|
|
||||||
|
## Task List
|
||||||
|
|
||||||
|
- [ ] Task 1
|
||||||
|
- [ ] Task 2
|
||||||
|
|
||||||
|
## Estimation
|
||||||
|
|
||||||
|
| Task | Estimate |
|
||||||
|
|------|----------|
|
||||||
|
| Task 1 | TBD |
|
||||||
|
| Task 2 | TBD |
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "specify-cli"
|
name = "specify-cli"
|
||||||
version = "0.0.20"
|
version = "0.3.0"
|
||||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typer",
|
"typer",
|
||||||
|
"click>=8.1",
|
||||||
"rich",
|
"rich",
|
||||||
"httpx[socks]",
|
"httpx[socks]",
|
||||||
"platformdirs",
|
"platformdirs",
|
||||||
"readchar",
|
"readchar",
|
||||||
"truststore>=0.10.4",
|
"truststore>=0.10.4",
|
||||||
|
"pyyaml>=6.0",
|
||||||
|
"packaging>=23.0",
|
||||||
|
"pathspec>=0.12.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
@@ -22,3 +26,29 @@ build-backend = "hatchling.build"
|
|||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/specify_cli"]
|
packages = ["src/specify_cli"]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
test = [
|
||||||
|
"pytest>=7.0",
|
||||||
|
"pytest-cov>=4.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = ["tests"]
|
||||||
|
python_files = ["test_*.py"]
|
||||||
|
python_classes = ["Test*"]
|
||||||
|
python_functions = ["test_*"]
|
||||||
|
addopts = [
|
||||||
|
"-v",
|
||||||
|
"--strict-markers",
|
||||||
|
"--tb=short",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = ["src"]
|
||||||
|
omit = ["*/tests/*", "*/__pycache__/*"]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
precision = 2
|
||||||
|
show_missing = true
|
||||||
|
skip_covered = false
|
||||||
|
|
||||||
|
|||||||
@@ -75,19 +75,32 @@ EOF
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Source common functions
|
# 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"
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
|
||||||
# Get feature paths and validate branch
|
# Get feature paths and validate branch
|
||||||
eval $(get_feature_paths)
|
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
||||||
|
eval "$_paths_output"
|
||||||
|
unset _paths_output
|
||||||
check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
||||||
|
|
||||||
# If paths-only mode, output paths and exit (support JSON + paths-only combined)
|
# If paths-only mode, output paths and exit (support JSON + paths-only combined)
|
||||||
if $PATHS_ONLY; then
|
if $PATHS_ONLY; then
|
||||||
if $JSON_MODE; then
|
if $JSON_MODE; then
|
||||||
# Minimal JSON paths payload (no validation performed)
|
# Minimal JSON paths payload (no validation performed)
|
||||||
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
|
if has_jq; then
|
||||||
"$REPO_ROOT" "$CURRENT_BRANCH" "$FEATURE_DIR" "$FEATURE_SPEC" "$IMPL_PLAN" "$TASKS"
|
jq -cn \
|
||||||
|
--arg repo_root "$REPO_ROOT" \
|
||||||
|
--arg branch "$CURRENT_BRANCH" \
|
||||||
|
--arg feature_dir "$FEATURE_DIR" \
|
||||||
|
--arg feature_spec "$FEATURE_SPEC" \
|
||||||
|
--arg impl_plan "$IMPL_PLAN" \
|
||||||
|
--arg tasks "$TASKS" \
|
||||||
|
'{REPO_ROOT:$repo_root,BRANCH:$branch,FEATURE_DIR:$feature_dir,FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,TASKS:$tasks}'
|
||||||
|
else
|
||||||
|
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
|
||||||
|
"$(json_escape "$REPO_ROOT")" "$(json_escape "$CURRENT_BRANCH")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$TASKS")"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "REPO_ROOT: $REPO_ROOT"
|
echo "REPO_ROOT: $REPO_ROOT"
|
||||||
echo "BRANCH: $CURRENT_BRANCH"
|
echo "BRANCH: $CURRENT_BRANCH"
|
||||||
@@ -141,14 +154,25 @@ fi
|
|||||||
# Output results
|
# Output results
|
||||||
if $JSON_MODE; then
|
if $JSON_MODE; then
|
||||||
# Build JSON array of documents
|
# Build JSON array of documents
|
||||||
if [[ ${#docs[@]} -eq 0 ]]; then
|
if has_jq; then
|
||||||
json_docs="[]"
|
if [[ ${#docs[@]} -eq 0 ]]; then
|
||||||
|
json_docs="[]"
|
||||||
|
else
|
||||||
|
json_docs=$(printf '%s\n' "${docs[@]}" | jq -R . | jq -s .)
|
||||||
|
fi
|
||||||
|
jq -cn \
|
||||||
|
--arg feature_dir "$FEATURE_DIR" \
|
||||||
|
--argjson docs "$json_docs" \
|
||||||
|
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs}'
|
||||||
else
|
else
|
||||||
json_docs=$(printf '"%s",' "${docs[@]}")
|
if [[ ${#docs[@]} -eq 0 ]]; then
|
||||||
json_docs="[${json_docs%,}]"
|
json_docs="[]"
|
||||||
|
else
|
||||||
|
json_docs=$(printf '"%s",' "${docs[@]}")
|
||||||
|
json_docs="[${json_docs%,}]"
|
||||||
|
fi
|
||||||
|
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$FEATURE_DIR" "$json_docs"
|
|
||||||
else
|
else
|
||||||
# Text output
|
# Text output
|
||||||
echo "FEATURE_DIR:$FEATURE_DIR"
|
echo "FEATURE_DIR:$FEATURE_DIR"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ get_repo_root() {
|
|||||||
git rev-parse --show-toplevel
|
git rev-parse --show-toplevel
|
||||||
else
|
else
|
||||||
# Fall back to script location for non-git repos
|
# Fall back to script location for non-git repos
|
||||||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
(cd "$script_dir/../../.." && pwd)
|
(cd "$script_dir/../../.." && pwd)
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ find_feature_dir_by_prefix() {
|
|||||||
# Multiple matches - this shouldn't happen with proper naming convention
|
# Multiple matches - this shouldn't happen with proper naming convention
|
||||||
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
|
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
|
||||||
echo "Please ensure only one spec directory exists per numeric prefix." >&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
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,23 +134,120 @@ get_feature_paths() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Use prefix-based lookup to support multiple branches per spec
|
# Use prefix-based lookup to support multiple branches per spec
|
||||||
local feature_dir=$(find_feature_dir_by_prefix "$repo_root" "$current_branch")
|
local feature_dir
|
||||||
|
if ! feature_dir=$(find_feature_dir_by_prefix "$repo_root" "$current_branch"); then
|
||||||
|
echo "ERROR: Failed to resolve feature directory" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
cat <<EOF
|
# Use printf '%q' to safely quote values, preventing shell injection
|
||||||
REPO_ROOT='$repo_root'
|
# via crafted branch names or paths containing special characters
|
||||||
CURRENT_BRANCH='$current_branch'
|
printf 'REPO_ROOT=%q\n' "$repo_root"
|
||||||
HAS_GIT='$has_git_repo'
|
printf 'CURRENT_BRANCH=%q\n' "$current_branch"
|
||||||
FEATURE_DIR='$feature_dir'
|
printf 'HAS_GIT=%q\n' "$has_git_repo"
|
||||||
FEATURE_SPEC='$feature_dir/spec.md'
|
printf 'FEATURE_DIR=%q\n' "$feature_dir"
|
||||||
IMPL_PLAN='$feature_dir/plan.md'
|
printf 'FEATURE_SPEC=%q\n' "$feature_dir/spec.md"
|
||||||
TASKS='$feature_dir/tasks.md'
|
printf 'IMPL_PLAN=%q\n' "$feature_dir/plan.md"
|
||||||
RESEARCH='$feature_dir/research.md'
|
printf 'TASKS=%q\n' "$feature_dir/tasks.md"
|
||||||
DATA_MODEL='$feature_dir/data-model.md'
|
printf 'RESEARCH=%q\n' "$feature_dir/research.md"
|
||||||
QUICKSTART='$feature_dir/quickstart.md'
|
printf 'DATA_MODEL=%q\n' "$feature_dir/data-model.md"
|
||||||
CONTRACTS_DIR='$feature_dir/contracts'
|
printf 'QUICKSTART=%q\n' "$feature_dir/quickstart.md"
|
||||||
EOF
|
printf 'CONTRACTS_DIR=%q\n' "$feature_dir/contracts"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if jq is available for safe JSON construction
|
||||||
|
has_jq() {
|
||||||
|
command -v jq >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
|
||||||
|
# Handles backslash, double-quote, and control characters (newline, tab, carriage return).
|
||||||
|
json_escape() {
|
||||||
|
local s="$1"
|
||||||
|
s="${s//\\/\\\\}"
|
||||||
|
s="${s//\"/\\\"}"
|
||||||
|
s="${s//$'\n'/\\n}"
|
||||||
|
s="${s//$'\t'/\\t}"
|
||||||
|
s="${s//$'\r'/\\r}"
|
||||||
|
printf '%s' "$s"
|
||||||
}
|
}
|
||||||
|
|
||||||
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||||
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||||
|
|
||||||
|
# Resolve a template name to a file path using the priority stack:
|
||||||
|
# 1. .specify/templates/overrides/
|
||||||
|
# 2. .specify/presets/<preset-id>/templates/ (sorted by priority from .registry)
|
||||||
|
# 3. .specify/extensions/<ext-id>/templates/
|
||||||
|
# 4. .specify/templates/ (core)
|
||||||
|
resolve_template() {
|
||||||
|
local template_name="$1"
|
||||||
|
local repo_root="$2"
|
||||||
|
local base="$repo_root/.specify/templates"
|
||||||
|
|
||||||
|
# Priority 1: Project overrides
|
||||||
|
local override="$base/overrides/${template_name}.md"
|
||||||
|
[ -f "$override" ] && echo "$override" && return 0
|
||||||
|
|
||||||
|
# Priority 2: Installed presets (sorted by priority from .registry)
|
||||||
|
local presets_dir="$repo_root/.specify/presets"
|
||||||
|
if [ -d "$presets_dir" ]; then
|
||||||
|
local registry_file="$presets_dir/.registry"
|
||||||
|
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
||||||
|
# Read preset IDs sorted by priority (lower number = higher precedence)
|
||||||
|
local sorted_presets
|
||||||
|
sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
|
||||||
|
import json, sys, os
|
||||||
|
try:
|
||||||
|
with open(os.environ['SPECKIT_REGISTRY']) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
presets = data.get('presets', {})
|
||||||
|
for pid, meta in sorted(presets.items(), key=lambda x: x[1].get('priority', 10)):
|
||||||
|
print(pid)
|
||||||
|
except Exception:
|
||||||
|
sys.exit(1)
|
||||||
|
" 2>/dev/null)
|
||||||
|
if [ $? -eq 0 ] && [ -n "$sorted_presets" ]; then
|
||||||
|
while IFS= read -r preset_id; do
|
||||||
|
local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||||
|
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||||
|
done <<< "$sorted_presets"
|
||||||
|
else
|
||||||
|
# python3 returned empty list — fall through to directory scan
|
||||||
|
for preset in "$presets_dir"/*/; do
|
||||||
|
[ -d "$preset" ] || continue
|
||||||
|
local candidate="$preset/templates/${template_name}.md"
|
||||||
|
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Fallback: alphabetical directory order (no python3 available)
|
||||||
|
for preset in "$presets_dir"/*/; do
|
||||||
|
[ -d "$preset" ] || continue
|
||||||
|
local candidate="$preset/templates/${template_name}.md"
|
||||||
|
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Priority 3: Extension-provided templates
|
||||||
|
local ext_dir="$repo_root/.specify/extensions"
|
||||||
|
if [ -d "$ext_dir" ]; then
|
||||||
|
for ext in "$ext_dir"/*/; do
|
||||||
|
[ -d "$ext" ] || continue
|
||||||
|
# Skip hidden directories (e.g. .backup, .cache)
|
||||||
|
case "$(basename "$ext")" in .*) continue;; esac
|
||||||
|
local candidate="$ext/templates/${template_name}.md"
|
||||||
|
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Priority 4: Core templates
|
||||||
|
local core="$base/${template_name}.md"
|
||||||
|
[ -f "$core" ] && echo "$core" && return 0
|
||||||
|
|
||||||
|
# Return success with empty output so callers using set -e don't abort;
|
||||||
|
# callers check [ -n "$TEMPLATE" ] to detect "not found".
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ set -e
|
|||||||
|
|
||||||
JSON_MODE=false
|
JSON_MODE=false
|
||||||
SHORT_NAME=""
|
SHORT_NAME=""
|
||||||
|
BRANCH_NUMBER=""
|
||||||
ARGS=()
|
ARGS=()
|
||||||
i=1
|
i=1
|
||||||
while [ $i -le $# ]; do
|
while [ $i -le $# ]; do
|
||||||
@@ -26,17 +27,31 @@ while [ $i -le $# ]; do
|
|||||||
fi
|
fi
|
||||||
SHORT_NAME="$next_arg"
|
SHORT_NAME="$next_arg"
|
||||||
;;
|
;;
|
||||||
|
--number)
|
||||||
|
if [ $((i + 1)) -gt $# ]; then
|
||||||
|
echo 'Error: --number requires a value' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
next_arg="${!i}"
|
||||||
|
if [[ "$next_arg" == --* ]]; then
|
||||||
|
echo 'Error: --number requires a value' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BRANCH_NUMBER="$next_arg"
|
||||||
|
;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
echo "Usage: $0 [--json] [--short-name <name>] <feature_description>"
|
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " --json Output in JSON format"
|
echo " --json Output in JSON format"
|
||||||
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
|
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
|
||||||
|
echo " --number N Specify branch number manually (overrides auto-detection)"
|
||||||
echo " --help, -h Show this help message"
|
echo " --help, -h Show this help message"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Examples:"
|
echo "Examples:"
|
||||||
echo " $0 'Add user authentication system' --short-name 'user-auth'"
|
echo " $0 'Add user authentication system' --short-name 'user-auth'"
|
||||||
echo " $0 'Implement OAuth2 integration for API'"
|
echo " $0 'Implement OAuth2 integration for API' --number 5"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -48,7 +63,14 @@ done
|
|||||||
|
|
||||||
FEATURE_DESCRIPTION="${ARGS[*]}"
|
FEATURE_DESCRIPTION="${ARGS[*]}"
|
||||||
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||||
echo "Usage: $0 [--json] [--short-name <name>] <feature_description>" >&2
|
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Trim whitespace and validate description is not empty (e.g., user passed only whitespace)
|
||||||
|
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
|
||||||
|
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||||
|
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -65,10 +87,97 @@ find_repo_root() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to get highest number from specs directory
|
||||||
|
get_highest_from_specs() {
|
||||||
|
local specs_dir="$1"
|
||||||
|
local highest=0
|
||||||
|
|
||||||
|
if [ -d "$specs_dir" ]; then
|
||||||
|
for dir in "$specs_dir"/*; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
dirname=$(basename "$dir")
|
||||||
|
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
|
||||||
|
number=$((10#$number))
|
||||||
|
if [ "$number" -gt "$highest" ]; then
|
||||||
|
highest=$number
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$highest"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get highest number from git branches
|
||||||
|
get_highest_from_branches() {
|
||||||
|
local highest=0
|
||||||
|
|
||||||
|
# Get all branches (local and remote)
|
||||||
|
branches=$(git branch -a 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -n "$branches" ]; then
|
||||||
|
while IFS= read -r branch; do
|
||||||
|
# Clean branch name: remove leading markers and remote prefixes
|
||||||
|
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
|
||||||
|
|
||||||
|
# Extract feature number if branch matches pattern ###-*
|
||||||
|
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
|
||||||
|
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
|
||||||
|
number=$((10#$number))
|
||||||
|
if [ "$number" -gt "$highest" ]; then
|
||||||
|
highest=$number
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$branches"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$highest"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check existing branches (local and remote) and return next available number
|
||||||
|
check_existing_branches() {
|
||||||
|
local 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/-$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
|
||||||
|
json_escape() {
|
||||||
|
local s="$1"
|
||||||
|
s="${s//\\/\\\\}"
|
||||||
|
s="${s//\"/\\\"}"
|
||||||
|
s="${s//$'\n'/\\n}"
|
||||||
|
s="${s//$'\t'/\\t}"
|
||||||
|
s="${s//$'\r'/\\r}"
|
||||||
|
printf '%s' "$s"
|
||||||
|
}
|
||||||
|
|
||||||
# Resolve repository root. Prefer git information when available, but fall back
|
# Resolve repository root. Prefer git information when available, but fall back
|
||||||
# to searching for repository markers so the workflow still functions in repositories that
|
# to searching for repository markers so the workflow still functions in repositories that
|
||||||
# were initialised with --no-git.
|
# were initialised with --no-git.
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
|
||||||
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
if git rev-parse --show-toplevel >/dev/null 2>&1; then
|
||||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||||
@@ -87,20 +196,6 @@ cd "$REPO_ROOT"
|
|||||||
SPECS_DIR="$REPO_ROOT/specs"
|
SPECS_DIR="$REPO_ROOT/specs"
|
||||||
mkdir -p "$SPECS_DIR"
|
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
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
NEXT=$((HIGHEST + 1))
|
|
||||||
FEATURE_NUM=$(printf "%03d" "$NEXT")
|
|
||||||
|
|
||||||
# Function to generate branch name with stop word filtering and length filtering
|
# Function to generate branch name with stop word filtering and length filtering
|
||||||
generate_branch_name() {
|
generate_branch_name() {
|
||||||
local description="$1"
|
local description="$1"
|
||||||
@@ -144,19 +239,34 @@ generate_branch_name() {
|
|||||||
echo "$result"
|
echo "$result"
|
||||||
else
|
else
|
||||||
# Fallback to original logic if no meaningful words found
|
# Fallback to original logic if no meaningful words found
|
||||||
echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//' | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
|
local cleaned=$(clean_branch_name "$description")
|
||||||
|
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Generate branch name
|
# Generate branch name
|
||||||
if [ -n "$SHORT_NAME" ]; then
|
if [ -n "$SHORT_NAME" ]; then
|
||||||
# Use provided short name, just clean it up
|
# Use provided short name, just clean it up
|
||||||
BRANCH_SUFFIX=$(echo "$SHORT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
|
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
|
||||||
else
|
else
|
||||||
# Generate from description with smart filtering
|
# Generate from description with smart filtering
|
||||||
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
|
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Determine branch number
|
||||||
|
if [ -z "$BRANCH_NUMBER" ]; then
|
||||||
|
if [ "$HAS_GIT" = true ]; then
|
||||||
|
# Check existing branches on remotes
|
||||||
|
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR")
|
||||||
|
else
|
||||||
|
# Fall back to local directory check
|
||||||
|
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
|
||||||
|
BRANCH_NUMBER=$((HIGHEST + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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}"
|
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
||||||
|
|
||||||
# GitHub enforces a 244-byte limit on branch names
|
# GitHub enforces a 244-byte limit on branch names
|
||||||
@@ -181,7 +291,16 @@ if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$HAS_GIT" = true ]; then
|
if [ "$HAS_GIT" = true ]; then
|
||||||
git checkout -b "$BRANCH_NAME"
|
if ! git checkout -b "$BRANCH_NAME" 2>/dev/null; then
|
||||||
|
# Check if branch already exists
|
||||||
|
if git branch --list "$BRANCH_NAME" | grep -q .; then
|
||||||
|
>&2 echo "Error: Branch '$BRANCH_NAME' already exists. Please use a different feature name or specify a different number with --number."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
>&2 echo "Error: Failed to create git branch '$BRANCH_NAME'. Please check your git configuration and try again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
|
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
|
||||||
fi
|
fi
|
||||||
@@ -189,18 +308,26 @@ fi
|
|||||||
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
|
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
|
||||||
mkdir -p "$FEATURE_DIR"
|
mkdir -p "$FEATURE_DIR"
|
||||||
|
|
||||||
TEMPLATE="$REPO_ROOT/.specify/templates/spec-template.md"
|
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT")
|
||||||
SPEC_FILE="$FEATURE_DIR/spec.md"
|
SPEC_FILE="$FEATURE_DIR/spec.md"
|
||||||
if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
|
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
|
||||||
|
|
||||||
# Set the SPECIFY_FEATURE environment variable for the current session
|
# Inform the user how to persist the feature variable in their own shell
|
||||||
export SPECIFY_FEATURE="$BRANCH_NAME"
|
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2
|
||||||
|
|
||||||
if $JSON_MODE; then
|
if $JSON_MODE; then
|
||||||
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM"
|
if command -v jq >/dev/null 2>&1; then
|
||||||
|
jq -cn \
|
||||||
|
--arg branch_name "$BRANCH_NAME" \
|
||||||
|
--arg spec_file "$SPEC_FILE" \
|
||||||
|
--arg feature_num "$FEATURE_NUM" \
|
||||||
|
'{BRANCH_NAME:$branch_name,SPEC_FILE:$spec_file,FEATURE_NUM:$feature_num}'
|
||||||
|
else
|
||||||
|
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$(json_escape "$BRANCH_NAME")" "$(json_escape "$SPEC_FILE")" "$(json_escape "$FEATURE_NUM")"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "BRANCH_NAME: $BRANCH_NAME"
|
echo "BRANCH_NAME: $BRANCH_NAME"
|
||||||
echo "SPEC_FILE: $SPEC_FILE"
|
echo "SPEC_FILE: $SPEC_FILE"
|
||||||
echo "FEATURE_NUM: $FEATURE_NUM"
|
echo "FEATURE_NUM: $FEATURE_NUM"
|
||||||
echo "SPECIFY_FEATURE environment variable set to: $BRANCH_NAME"
|
printf '# To persist in your shell: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ for arg in "$@"; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Get script directory and load common functions
|
# 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"
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
|
||||||
# Get all paths and variables from common functions
|
# Get all paths and variables from common functions
|
||||||
eval $(get_feature_paths)
|
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
||||||
|
eval "$_paths_output"
|
||||||
|
unset _paths_output
|
||||||
|
|
||||||
# Check if we're on a proper feature branch (only for git repos)
|
# Check if we're on a proper feature branch (only for git repos)
|
||||||
check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
||||||
@@ -37,20 +39,30 @@ check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
|
|||||||
mkdir -p "$FEATURE_DIR"
|
mkdir -p "$FEATURE_DIR"
|
||||||
|
|
||||||
# Copy plan template if it exists
|
# Copy plan template if it exists
|
||||||
TEMPLATE="$REPO_ROOT/.specify/templates/plan-template.md"
|
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT")
|
||||||
if [[ -f "$TEMPLATE" ]]; then
|
if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then
|
||||||
cp "$TEMPLATE" "$IMPL_PLAN"
|
cp "$TEMPLATE" "$IMPL_PLAN"
|
||||||
echo "Copied plan template to $IMPL_PLAN"
|
echo "Copied plan template to $IMPL_PLAN"
|
||||||
else
|
else
|
||||||
echo "Warning: Plan template not found at $TEMPLATE"
|
echo "Warning: Plan template not found"
|
||||||
# Create a basic plan file if template doesn't exist
|
# Create a basic plan file if template doesn't exist
|
||||||
touch "$IMPL_PLAN"
|
touch "$IMPL_PLAN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Output results
|
# Output results
|
||||||
if $JSON_MODE; then
|
if $JSON_MODE; then
|
||||||
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","SPECS_DIR":"%s","BRANCH":"%s","HAS_GIT":"%s"}\n' \
|
if has_jq; then
|
||||||
"$FEATURE_SPEC" "$IMPL_PLAN" "$FEATURE_DIR" "$CURRENT_BRANCH" "$HAS_GIT"
|
jq -cn \
|
||||||
|
--arg feature_spec "$FEATURE_SPEC" \
|
||||||
|
--arg impl_plan "$IMPL_PLAN" \
|
||||||
|
--arg specs_dir "$FEATURE_DIR" \
|
||||||
|
--arg branch "$CURRENT_BRANCH" \
|
||||||
|
--arg has_git "$HAS_GIT" \
|
||||||
|
'{FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,SPECS_DIR:$specs_dir,BRANCH:$branch,HAS_GIT:$has_git}'
|
||||||
|
else
|
||||||
|
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","SPECS_DIR":"%s","BRANCH":"%s","HAS_GIT":"%s"}\n' \
|
||||||
|
"$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$CURRENT_BRANCH")" "$(json_escape "$HAS_GIT")"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "FEATURE_SPEC: $FEATURE_SPEC"
|
echo "FEATURE_SPEC: $FEATURE_SPEC"
|
||||||
echo "IMPL_PLAN: $IMPL_PLAN"
|
echo "IMPL_PLAN: $IMPL_PLAN"
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
#
|
#
|
||||||
# 5. Multi-Agent Support
|
# 5. Multi-Agent Support
|
||||||
# - Handles agent-specific file paths and naming conventions
|
# - Handles agent-specific file paths and naming conventions
|
||||||
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Amp, 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, Tabnine CLI, Kiro CLI, Mistral Vibe, Kimi Code, Antigravity or Generic
|
||||||
# - Can update single agents or all existing agent files
|
# - Can update single agents or all existing agent files
|
||||||
# - Creates default Claude file if no agent files exist
|
# - Creates default Claude file if no agent files exist
|
||||||
#
|
#
|
||||||
# Usage: ./update-agent-context.sh [agent_type]
|
# Usage: ./update-agent-context.sh [agent_type]
|
||||||
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|q
|
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|generic
|
||||||
# Leave empty to update all existing agent files
|
# Leave empty to update all existing agent files
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -49,11 +49,13 @@ set -o pipefail
|
|||||||
#==============================================================================
|
#==============================================================================
|
||||||
|
|
||||||
# Get script directory and load common functions
|
# 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"
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
|
||||||
# Get all paths and variables from common functions
|
# Get all paths and variables from common functions
|
||||||
eval $(get_feature_paths)
|
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
||||||
|
eval "$_paths_output"
|
||||||
|
unset _paths_output
|
||||||
|
|
||||||
NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code
|
NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code
|
||||||
AGENT_TYPE="${1:-}"
|
AGENT_TYPE="${1:-}"
|
||||||
@@ -61,7 +63,7 @@ AGENT_TYPE="${1:-}"
|
|||||||
# Agent-specific file paths
|
# Agent-specific file paths
|
||||||
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
|
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
|
||||||
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
|
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
|
||||||
COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
|
COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md"
|
||||||
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
||||||
QWEN_FILE="$REPO_ROOT/QWEN.md"
|
QWEN_FILE="$REPO_ROOT/QWEN.md"
|
||||||
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
|
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
|
||||||
@@ -70,8 +72,17 @@ KILOCODE_FILE="$REPO_ROOT/.kilocode/rules/specify-rules.md"
|
|||||||
AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
|
AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
|
||||||
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
|
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
|
||||||
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
|
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
|
||||||
AMP_FILE="$REPO_ROOT/AGENTS.md"
|
QODER_FILE="$REPO_ROOT/QODER.md"
|
||||||
Q_FILE="$REPO_ROOT/AGENTS.md"
|
# AMP, Kiro CLI, and IBM Bob all share AGENTS.md — use AGENTS_FILE to avoid
|
||||||
|
# updating the same file multiple times.
|
||||||
|
AMP_FILE="$AGENTS_FILE"
|
||||||
|
SHAI_FILE="$REPO_ROOT/SHAI.md"
|
||||||
|
TABNINE_FILE="$REPO_ROOT/TABNINE.md"
|
||||||
|
KIRO_FILE="$AGENTS_FILE"
|
||||||
|
AGY_FILE="$REPO_ROOT/.agent/rules/specify-rules.md"
|
||||||
|
BOB_FILE="$AGENTS_FILE"
|
||||||
|
VIBE_FILE="$REPO_ROOT/.vibe/agents/specify-agents.md"
|
||||||
|
KIMI_FILE="$REPO_ROOT/KIMI.md"
|
||||||
|
|
||||||
# Template file
|
# Template file
|
||||||
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
|
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
|
||||||
@@ -105,6 +116,8 @@ log_warning() {
|
|||||||
# Cleanup function for temporary files
|
# Cleanup function for temporary files
|
||||||
cleanup() {
|
cleanup() {
|
||||||
local exit_code=$?
|
local exit_code=$?
|
||||||
|
# Disarm traps to prevent re-entrant loop
|
||||||
|
trap - EXIT INT TERM
|
||||||
rm -f /tmp/agent_update_*_$$
|
rm -f /tmp/agent_update_*_$$
|
||||||
rm -f /tmp/manual_additions_$$
|
rm -f /tmp/manual_additions_$$
|
||||||
exit $exit_code
|
exit $exit_code
|
||||||
@@ -351,6 +364,15 @@ create_new_agent_file() {
|
|||||||
# Clean up backup files
|
# Clean up backup files
|
||||||
rm -f "$temp_file.bak" "$temp_file.bak2"
|
rm -f "$temp_file.bak" "$temp_file.bak2"
|
||||||
|
|
||||||
|
# Prepend Cursor frontmatter for .mdc files so rules are auto-included
|
||||||
|
if [[ "$target_file" == *.mdc ]]; then
|
||||||
|
local frontmatter_file
|
||||||
|
frontmatter_file=$(mktemp) || return 1
|
||||||
|
printf '%s\n' "---" "description: Project Development Guidelines" "globs: [\"**/*\"]" "alwaysApply: true" "---" "" > "$frontmatter_file"
|
||||||
|
cat "$temp_file" >> "$frontmatter_file"
|
||||||
|
mv "$frontmatter_file" "$temp_file"
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,12 +413,25 @@ update_existing_agent_file() {
|
|||||||
new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB"
|
new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB"
|
||||||
fi
|
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
|
# Process file line by line
|
||||||
local in_tech_section=false
|
local in_tech_section=false
|
||||||
local in_changes_section=false
|
local in_changes_section=false
|
||||||
local tech_entries_added=false
|
local tech_entries_added=false
|
||||||
local changes_entries_added=false
|
local changes_entries_added=false
|
||||||
local existing_changes_count=0
|
local existing_changes_count=0
|
||||||
|
local file_ended=false
|
||||||
|
|
||||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
# Handle Active Technologies section
|
# Handle Active Technologies section
|
||||||
@@ -447,7 +482,7 @@ update_existing_agent_file() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Update timestamp
|
# Update timestamp
|
||||||
if [[ "$line" =~ \*\*Last\ updated\*\*:.*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then
|
if [[ "$line" =~ (\*\*)?Last\ updated(\*\*)?:.*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then
|
||||||
echo "$line" | sed "s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/$current_date/" >> "$temp_file"
|
echo "$line" | sed "s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/$current_date/" >> "$temp_file"
|
||||||
else
|
else
|
||||||
echo "$line" >> "$temp_file"
|
echo "$line" >> "$temp_file"
|
||||||
@@ -457,6 +492,33 @@ update_existing_agent_file() {
|
|||||||
# Post-loop check: if we're still in the Active Technologies section and haven't added new entries
|
# 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
|
if [[ $in_tech_section == true ]] && [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
||||||
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
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
|
||||||
|
|
||||||
|
# Ensure Cursor .mdc files have YAML frontmatter for auto-inclusion
|
||||||
|
if [[ "$target_file" == *.mdc ]]; then
|
||||||
|
if ! head -1 "$temp_file" | grep -q '^---'; then
|
||||||
|
local frontmatter_file
|
||||||
|
frontmatter_file=$(mktemp) || { rm -f "$temp_file"; return 1; }
|
||||||
|
printf '%s\n' "---" "description: Project Development Guidelines" "globs: [\"**/*\"]" "alwaysApply: true" "---" "" > "$frontmatter_file"
|
||||||
|
cat "$temp_file" >> "$frontmatter_file"
|
||||||
|
mv "$frontmatter_file" "$temp_file"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Move temp file to target atomically
|
# Move temp file to target atomically
|
||||||
@@ -551,50 +613,74 @@ update_specific_agent() {
|
|||||||
|
|
||||||
case "$agent_type" in
|
case "$agent_type" in
|
||||||
claude)
|
claude)
|
||||||
update_agent_file "$CLAUDE_FILE" "Claude Code"
|
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
|
||||||
;;
|
;;
|
||||||
gemini)
|
gemini)
|
||||||
update_agent_file "$GEMINI_FILE" "Gemini CLI"
|
update_agent_file "$GEMINI_FILE" "Gemini CLI" || return 1
|
||||||
;;
|
;;
|
||||||
copilot)
|
copilot)
|
||||||
update_agent_file "$COPILOT_FILE" "GitHub Copilot"
|
update_agent_file "$COPILOT_FILE" "GitHub Copilot" || return 1
|
||||||
;;
|
;;
|
||||||
cursor-agent)
|
cursor-agent)
|
||||||
update_agent_file "$CURSOR_FILE" "Cursor IDE"
|
update_agent_file "$CURSOR_FILE" "Cursor IDE" || return 1
|
||||||
;;
|
;;
|
||||||
qwen)
|
qwen)
|
||||||
update_agent_file "$QWEN_FILE" "Qwen Code"
|
update_agent_file "$QWEN_FILE" "Qwen Code" || return 1
|
||||||
;;
|
;;
|
||||||
opencode)
|
opencode)
|
||||||
update_agent_file "$AGENTS_FILE" "opencode"
|
update_agent_file "$AGENTS_FILE" "opencode" || return 1
|
||||||
;;
|
;;
|
||||||
codex)
|
codex)
|
||||||
update_agent_file "$AGENTS_FILE" "Codex CLI"
|
update_agent_file "$AGENTS_FILE" "Codex CLI" || return 1
|
||||||
;;
|
;;
|
||||||
windsurf)
|
windsurf)
|
||||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
update_agent_file "$WINDSURF_FILE" "Windsurf" || return 1
|
||||||
;;
|
;;
|
||||||
kilocode)
|
kilocode)
|
||||||
update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
update_agent_file "$KILOCODE_FILE" "Kilo Code" || return 1
|
||||||
;;
|
;;
|
||||||
auggie)
|
auggie)
|
||||||
update_agent_file "$AUGGIE_FILE" "Auggie CLI"
|
update_agent_file "$AUGGIE_FILE" "Auggie CLI" || return 1
|
||||||
;;
|
;;
|
||||||
roo)
|
roo)
|
||||||
update_agent_file "$ROO_FILE" "Roo Code"
|
update_agent_file "$ROO_FILE" "Roo Code" || return 1
|
||||||
;;
|
;;
|
||||||
codebuddy)
|
codebuddy)
|
||||||
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI" || return 1
|
||||||
|
;;
|
||||||
|
qodercli)
|
||||||
|
update_agent_file "$QODER_FILE" "Qoder CLI" || return 1
|
||||||
;;
|
;;
|
||||||
amp)
|
amp)
|
||||||
update_agent_file "$AMP_FILE" "Amp"
|
update_agent_file "$AMP_FILE" "Amp" || return 1
|
||||||
;;
|
;;
|
||||||
q)
|
shai)
|
||||||
update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
|
update_agent_file "$SHAI_FILE" "SHAI" || return 1
|
||||||
|
;;
|
||||||
|
tabnine)
|
||||||
|
update_agent_file "$TABNINE_FILE" "Tabnine CLI" || return 1
|
||||||
|
;;
|
||||||
|
kiro-cli)
|
||||||
|
update_agent_file "$KIRO_FILE" "Kiro CLI" || return 1
|
||||||
|
;;
|
||||||
|
agy)
|
||||||
|
update_agent_file "$AGY_FILE" "Antigravity" || return 1
|
||||||
|
;;
|
||||||
|
bob)
|
||||||
|
update_agent_file "$BOB_FILE" "IBM Bob" || return 1
|
||||||
|
;;
|
||||||
|
vibe)
|
||||||
|
update_agent_file "$VIBE_FILE" "Mistral Vibe" || return 1
|
||||||
|
;;
|
||||||
|
kimi)
|
||||||
|
update_agent_file "$KIMI_FILE" "Kimi Code" || return 1
|
||||||
|
;;
|
||||||
|
generic)
|
||||||
|
log_info "Generic agent: no predefined context file. Use the agent-specific update script for your agent."
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
log_error "Unknown agent type '$agent_type'"
|
log_error "Unknown agent type '$agent_type'"
|
||||||
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|q"
|
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|generic"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -602,72 +688,53 @@ update_specific_agent() {
|
|||||||
|
|
||||||
update_all_existing_agents() {
|
update_all_existing_agents() {
|
||||||
local found_agent=false
|
local found_agent=false
|
||||||
|
local _updated_paths=()
|
||||||
|
|
||||||
# Check each possible agent file and update if it exists
|
# Helper: skip non-existent files and files already updated (dedup by
|
||||||
if [[ -f "$CLAUDE_FILE" ]]; then
|
# realpath so that variables pointing to the same file — e.g. AMP_FILE,
|
||||||
update_agent_file "$CLAUDE_FILE" "Claude Code"
|
# KIRO_FILE, BOB_FILE all resolving to AGENTS_FILE — are only written once).
|
||||||
|
# Uses a linear array instead of associative array for bash 3.2 compatibility.
|
||||||
|
update_if_new() {
|
||||||
|
local file="$1" name="$2"
|
||||||
|
[[ -f "$file" ]] || return 0
|
||||||
|
local real_path
|
||||||
|
real_path=$(realpath "$file" 2>/dev/null || echo "$file")
|
||||||
|
local p
|
||||||
|
if [[ ${#_updated_paths[@]} -gt 0 ]]; then
|
||||||
|
for p in "${_updated_paths[@]}"; do
|
||||||
|
[[ "$p" == "$real_path" ]] && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
update_agent_file "$file" "$name" || return 1
|
||||||
|
_updated_paths+=("$real_path")
|
||||||
found_agent=true
|
found_agent=true
|
||||||
fi
|
}
|
||||||
|
|
||||||
if [[ -f "$GEMINI_FILE" ]]; then
|
update_if_new "$CLAUDE_FILE" "Claude Code"
|
||||||
update_agent_file "$GEMINI_FILE" "Gemini CLI"
|
update_if_new "$GEMINI_FILE" "Gemini CLI"
|
||||||
found_agent=true
|
update_if_new "$COPILOT_FILE" "GitHub Copilot"
|
||||||
fi
|
update_if_new "$CURSOR_FILE" "Cursor IDE"
|
||||||
|
update_if_new "$QWEN_FILE" "Qwen Code"
|
||||||
if [[ -f "$COPILOT_FILE" ]]; then
|
update_if_new "$AGENTS_FILE" "Codex/opencode"
|
||||||
update_agent_file "$COPILOT_FILE" "GitHub Copilot"
|
update_if_new "$AMP_FILE" "Amp"
|
||||||
found_agent=true
|
update_if_new "$KIRO_FILE" "Kiro CLI"
|
||||||
fi
|
update_if_new "$BOB_FILE" "IBM Bob"
|
||||||
|
update_if_new "$WINDSURF_FILE" "Windsurf"
|
||||||
if [[ -f "$CURSOR_FILE" ]]; then
|
update_if_new "$KILOCODE_FILE" "Kilo Code"
|
||||||
update_agent_file "$CURSOR_FILE" "Cursor IDE"
|
update_if_new "$AUGGIE_FILE" "Auggie CLI"
|
||||||
found_agent=true
|
update_if_new "$ROO_FILE" "Roo Code"
|
||||||
fi
|
update_if_new "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
||||||
|
update_if_new "$SHAI_FILE" "SHAI"
|
||||||
if [[ -f "$QWEN_FILE" ]]; then
|
update_if_new "$TABNINE_FILE" "Tabnine CLI"
|
||||||
update_agent_file "$QWEN_FILE" "Qwen Code"
|
update_if_new "$QODER_FILE" "Qoder CLI"
|
||||||
found_agent=true
|
update_if_new "$AGY_FILE" "Antigravity"
|
||||||
fi
|
update_if_new "$VIBE_FILE" "Mistral Vibe"
|
||||||
|
update_if_new "$KIMI_FILE" "Kimi Code"
|
||||||
if [[ -f "$AGENTS_FILE" ]]; then
|
|
||||||
update_agent_file "$AGENTS_FILE" "Codex/opencode"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$WINDSURF_FILE" ]]; then
|
|
||||||
update_agent_file "$WINDSURF_FILE" "Windsurf"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$KILOCODE_FILE" ]]; then
|
|
||||||
update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$AUGGIE_FILE" ]]; then
|
|
||||||
update_agent_file "$AUGGIE_FILE" "Auggie CLI"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$ROO_FILE" ]]; then
|
|
||||||
update_agent_file "$ROO_FILE" "Roo Code"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$CODEBUDDY_FILE" ]]; then
|
|
||||||
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$Q_FILE" ]]; then
|
|
||||||
update_agent_file "$Q_FILE" "Amazon Q Developer CLI"
|
|
||||||
found_agent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If no agent files exist, create a default Claude file
|
# If no agent files exist, create a default Claude file
|
||||||
if [[ "$found_agent" == false ]]; then
|
if [[ "$found_agent" == false ]]; then
|
||||||
log_info "No existing agent files found, creating default Claude file..."
|
log_info "No existing agent files found, creating default Claude file..."
|
||||||
update_agent_file "$CLAUDE_FILE" "Claude Code"
|
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
print_summary() {
|
print_summary() {
|
||||||
@@ -687,8 +754,7 @@ print_summary() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|generic]"
|
||||||
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|codebuddy|q]"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#==============================================================================
|
#==============================================================================
|
||||||
@@ -740,4 +806,3 @@ main() {
|
|||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
main "$@"
|
main "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -135,3 +135,70 @@ function Test-DirHasFiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Resolve a template name to a file path using the priority stack:
|
||||||
|
# 1. .specify/templates/overrides/
|
||||||
|
# 2. .specify/presets/<preset-id>/templates/ (sorted by priority from .registry)
|
||||||
|
# 3. .specify/extensions/<ext-id>/templates/
|
||||||
|
# 4. .specify/templates/ (core)
|
||||||
|
function Resolve-Template {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)][string]$TemplateName,
|
||||||
|
[Parameter(Mandatory=$true)][string]$RepoRoot
|
||||||
|
)
|
||||||
|
|
||||||
|
$base = Join-Path $RepoRoot '.specify/templates'
|
||||||
|
|
||||||
|
# Priority 1: Project overrides
|
||||||
|
$override = Join-Path $base "overrides/$TemplateName.md"
|
||||||
|
if (Test-Path $override) { return $override }
|
||||||
|
|
||||||
|
# Priority 2: Installed presets (sorted by priority from .registry)
|
||||||
|
$presetsDir = Join-Path $RepoRoot '.specify/presets'
|
||||||
|
if (Test-Path $presetsDir) {
|
||||||
|
$registryFile = Join-Path $presetsDir '.registry'
|
||||||
|
$sortedPresets = @()
|
||||||
|
if (Test-Path $registryFile) {
|
||||||
|
try {
|
||||||
|
$registryData = Get-Content $registryFile -Raw | ConvertFrom-Json
|
||||||
|
$presets = $registryData.presets
|
||||||
|
if ($presets) {
|
||||||
|
$sortedPresets = $presets.PSObject.Properties |
|
||||||
|
Sort-Object { if ($null -ne $_.Value.priority) { $_.Value.priority } else { 10 } } |
|
||||||
|
ForEach-Object { $_.Name }
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# Fallback: alphabetical directory order
|
||||||
|
$sortedPresets = @()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sortedPresets.Count -gt 0) {
|
||||||
|
foreach ($presetId in $sortedPresets) {
|
||||||
|
$candidate = Join-Path $presetsDir "$presetId/templates/$TemplateName.md"
|
||||||
|
if (Test-Path $candidate) { return $candidate }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# Fallback: alphabetical directory order
|
||||||
|
foreach ($preset in Get-ChildItem -Path $presetsDir -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '.*' }) {
|
||||||
|
$candidate = Join-Path $preset.FullName "templates/$TemplateName.md"
|
||||||
|
if (Test-Path $candidate) { return $candidate }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Priority 3: Extension-provided templates
|
||||||
|
$extDir = Join-Path $RepoRoot '.specify/extensions'
|
||||||
|
if (Test-Path $extDir) {
|
||||||
|
foreach ($ext in Get-ChildItem -Path $extDir -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '.*' } | Sort-Object Name) {
|
||||||
|
$candidate = Join-Path $ext.FullName "templates/$TemplateName.md"
|
||||||
|
if (Test-Path $candidate) { return $candidate }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Priority 4: Core templates
|
||||||
|
$core = Join-Path $base "$TemplateName.md"
|
||||||
|
if (Test-Path $core) { return $core }
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
param(
|
param(
|
||||||
[switch]$Json,
|
[switch]$Json,
|
||||||
[string]$ShortName,
|
[string]$ShortName,
|
||||||
|
[int]$Number = 0,
|
||||||
[switch]$Help,
|
[switch]$Help,
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
[Parameter(ValueFromRemainingArguments = $true)]
|
||||||
[string[]]$FeatureDescription
|
[string[]]$FeatureDescription
|
||||||
@@ -12,11 +13,12 @@ $ErrorActionPreference = 'Stop'
|
|||||||
|
|
||||||
# Show help if requested
|
# Show help if requested
|
||||||
if ($Help) {
|
if ($Help) {
|
||||||
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
|
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] [-Number N] <feature description>"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Options:"
|
Write-Host "Options:"
|
||||||
Write-Host " -Json Output in JSON format"
|
Write-Host " -Json Output in JSON format"
|
||||||
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
|
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
|
||||||
|
Write-Host " -Number N Specify branch number manually (overrides auto-detection)"
|
||||||
Write-Host " -Help Show this help message"
|
Write-Host " -Help Show this help message"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Examples:"
|
Write-Host "Examples:"
|
||||||
@@ -33,6 +35,12 @@ if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
|
|||||||
|
|
||||||
$featureDesc = ($FeatureDescription -join ' ').Trim()
|
$featureDesc = ($FeatureDescription -join ' ').Trim()
|
||||||
|
|
||||||
|
# Validate description is not empty after trimming (e.g., user passed only whitespace)
|
||||||
|
if ([string]::IsNullOrWhiteSpace($featureDesc)) {
|
||||||
|
Write-Error "Error: Feature description cannot be empty or contain only whitespace"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
# Resolve repository root. Prefer git information when available, but fall back
|
# Resolve repository root. Prefer git information when available, but fall back
|
||||||
# to searching for repository markers so the workflow still functions in repositories that
|
# to searching for repository markers so the workflow still functions in repositories that
|
||||||
# were initialized with --no-git.
|
# were initialized with --no-git.
|
||||||
@@ -56,12 +64,86 @@ function Find-RepositoryRoot {
|
|||||||
$current = $parent
|
$current = $parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-HighestNumberFromSpecs {
|
||||||
|
param([string]$SpecsDir)
|
||||||
|
|
||||||
|
$highest = 0
|
||||||
|
if (Test-Path $SpecsDir) {
|
||||||
|
Get-ChildItem -Path $SpecsDir -Directory | ForEach-Object {
|
||||||
|
if ($_.Name -match '^(\d+)') {
|
||||||
|
$num = [int]$matches[1]
|
||||||
|
if ($num -gt $highest) { $highest = $num }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $highest
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HighestNumberFromBranches {
|
||||||
|
param()
|
||||||
|
|
||||||
|
$highest = 0
|
||||||
|
try {
|
||||||
|
$branches = git branch -a 2>$null
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
foreach ($branch in $branches) {
|
||||||
|
# Clean branch name: remove leading markers and remote prefixes
|
||||||
|
$cleanBranch = $branch.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
|
||||||
|
|
||||||
|
# Extract feature number if branch matches pattern ###-*
|
||||||
|
if ($cleanBranch -match '^(\d+)-') {
|
||||||
|
$num = [int]$matches[1]
|
||||||
|
if ($num -gt $highest) { $highest = $num }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
# If git command fails, return 0
|
||||||
|
Write-Verbose "Could not check Git branches: $_"
|
||||||
|
}
|
||||||
|
return $highest
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-NextBranchNumber {
|
||||||
|
param(
|
||||||
|
[string]$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)
|
$fallbackRoot = (Find-RepositoryRoot -StartDir $PSScriptRoot)
|
||||||
if (-not $fallbackRoot) {
|
if (-not $fallbackRoot) {
|
||||||
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
|
Write-Error "Error: Could not determine repository root. Please run this script from within the repository."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Load common functions (includes Resolve-Template)
|
||||||
|
. "$PSScriptRoot/common.ps1"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$repoRoot = git rev-parse --show-toplevel 2>$null
|
$repoRoot = git rev-parse --show-toplevel 2>$null
|
||||||
if ($LASTEXITCODE -eq 0) {
|
if ($LASTEXITCODE -eq 0) {
|
||||||
@@ -79,18 +161,6 @@ Set-Location $repoRoot
|
|||||||
$specsDir = Join-Path $repoRoot 'specs'
|
$specsDir = Join-Path $repoRoot 'specs'
|
||||||
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
|
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
|
||||||
|
|
||||||
$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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$next = $highest + 1
|
|
||||||
$featureNum = ('{0:000}' -f $next)
|
|
||||||
|
|
||||||
# Function to generate branch name with stop word filtering and length filtering
|
# Function to generate branch name with stop word filtering and length filtering
|
||||||
function Get-BranchName {
|
function Get-BranchName {
|
||||||
param([string]$Description)
|
param([string]$Description)
|
||||||
@@ -130,7 +200,7 @@ function Get-BranchName {
|
|||||||
return $result
|
return $result
|
||||||
} else {
|
} else {
|
||||||
# Fallback to original logic if no meaningful words found
|
# Fallback to original logic if no meaningful words found
|
||||||
$result = $Description.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
$result = ConvertTo-CleanBranchName -Name $Description
|
||||||
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
|
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
|
||||||
return [string]::Join('-', $fallbackWords)
|
return [string]::Join('-', $fallbackWords)
|
||||||
}
|
}
|
||||||
@@ -139,12 +209,24 @@ function Get-BranchName {
|
|||||||
# Generate branch name
|
# Generate branch name
|
||||||
if ($ShortName) {
|
if ($ShortName) {
|
||||||
# Use provided short name, just clean it up
|
# Use provided short name, just clean it up
|
||||||
$branchSuffix = $ShortName.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
$branchSuffix = ConvertTo-CleanBranchName -Name $ShortName
|
||||||
} else {
|
} else {
|
||||||
# Generate from description with smart filtering
|
# Generate from description with smart filtering
|
||||||
$branchSuffix = Get-BranchName -Description $featureDesc
|
$branchSuffix = Get-BranchName -Description $featureDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Determine branch number
|
||||||
|
if ($Number -eq 0) {
|
||||||
|
if ($hasGit) {
|
||||||
|
# Check existing branches on remotes
|
||||||
|
$Number = Get-NextBranchNumber -SpecsDir $specsDir
|
||||||
|
} else {
|
||||||
|
# Fall back to local directory check
|
||||||
|
$Number = (Get-HighestNumberFromSpecs -SpecsDir $specsDir) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$featureNum = ('{0:000}' -f $Number)
|
||||||
$branchName = "$featureNum-$branchSuffix"
|
$branchName = "$featureNum-$branchSuffix"
|
||||||
|
|
||||||
# GitHub enforces a 244-byte limit on branch names
|
# GitHub enforces a 244-byte limit on branch names
|
||||||
@@ -169,10 +251,26 @@ if ($branchName.Length -gt $maxBranchLength) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($hasGit) {
|
if ($hasGit) {
|
||||||
|
$branchCreated = $false
|
||||||
try {
|
try {
|
||||||
git checkout -b $branchName | Out-Null
|
git checkout -q -b $branchName 2>$null | Out-Null
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
$branchCreated = $true
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Warning "Failed to create git branch: $branchName"
|
# Exception during git command
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $branchCreated) {
|
||||||
|
# Check if branch already exists
|
||||||
|
$existingBranch = git branch --list $branchName 2>$null
|
||||||
|
if ($existingBranch) {
|
||||||
|
Write-Error "Error: Branch '$branchName' already exists. Please use a different feature name or specify a different number with -Number."
|
||||||
|
exit 1
|
||||||
|
} else {
|
||||||
|
Write-Error "Error: Failed to create git branch '$branchName'. Please check your git configuration and try again."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Warning "[specify] Warning: Git repository not detected; skipped branch creation for $branchName"
|
Write-Warning "[specify] Warning: Git repository not detected; skipped branch creation for $branchName"
|
||||||
@@ -181,9 +279,9 @@ if ($hasGit) {
|
|||||||
$featureDir = Join-Path $specsDir $branchName
|
$featureDir = Join-Path $specsDir $branchName
|
||||||
New-Item -ItemType Directory -Path $featureDir -Force | Out-Null
|
New-Item -ItemType Directory -Path $featureDir -Force | Out-Null
|
||||||
|
|
||||||
$template = Join-Path $repoRoot '.specify/templates/spec-template.md'
|
$template = Resolve-Template -TemplateName 'spec-template' -RepoRoot $repoRoot
|
||||||
$specFile = Join-Path $featureDir 'spec.md'
|
$specFile = Join-Path $featureDir 'spec.md'
|
||||||
if (Test-Path $template) {
|
if ($template -and (Test-Path $template)) {
|
||||||
Copy-Item $template $specFile -Force
|
Copy-Item $template $specFile -Force
|
||||||
} else {
|
} else {
|
||||||
New-Item -ItemType File -Path $specFile | Out-Null
|
New-Item -ItemType File -Path $specFile | Out-Null
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ if (-not (Test-FeatureBranch -Branch $paths.CURRENT_BRANCH -HasGit $paths.HAS_GI
|
|||||||
New-Item -ItemType Directory -Path $paths.FEATURE_DIR -Force | Out-Null
|
New-Item -ItemType Directory -Path $paths.FEATURE_DIR -Force | Out-Null
|
||||||
|
|
||||||
# Copy plan template if it exists, otherwise note it or create empty file
|
# Copy plan template if it exists, otherwise note it or create empty file
|
||||||
$template = Join-Path $paths.REPO_ROOT '.specify/templates/plan-template.md'
|
$template = Resolve-Template -TemplateName 'plan-template' -RepoRoot $paths.REPO_ROOT
|
||||||
if (Test-Path $template) {
|
if ($template -and (Test-Path $template)) {
|
||||||
Copy-Item $template $paths.IMPL_PLAN -Force
|
Copy-Item $template $paths.IMPL_PLAN -Force
|
||||||
Write-Output "Copied plan template to $($paths.IMPL_PLAN)"
|
Write-Output "Copied plan template to $($paths.IMPL_PLAN)"
|
||||||
} else {
|
} else {
|
||||||
Write-Warning "Plan template not found at $template"
|
Write-Warning "Plan template not found"
|
||||||
# Create a basic plan file if template doesn't exist
|
# Create a basic plan file if template doesn't exist
|
||||||
New-Item -ItemType File -Path $paths.IMPL_PLAN -Force | Out-Null
|
New-Item -ItemType File -Path $paths.IMPL_PLAN -Force | Out-Null
|
||||||
}
|
}
|
||||||
@@ -59,4 +59,3 @@ if ($Json) {
|
|||||||
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
|
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
|
||||||
Write-Output "HAS_GIT: $($paths.HAS_GIT)"
|
Write-Output "HAS_GIT: $($paths.HAS_GIT)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh:
|
|||||||
2. Plan Data Extraction
|
2. Plan Data Extraction
|
||||||
3. Agent File Management (create from template or update existing)
|
3. Agent File Management (create from template or update existing)
|
||||||
4. Content Generation (technology stack, recent changes, timestamp)
|
4. Content Generation (technology stack, recent changes, timestamp)
|
||||||
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, amp, q)
|
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, kiro-cli, agy, bob, vibe, qodercli, kimi, generic)
|
||||||
|
|
||||||
.PARAMETER AgentType
|
.PARAMETER AgentType
|
||||||
Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
|
Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
|
||||||
@@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1
|
|||||||
#>
|
#>
|
||||||
param(
|
param(
|
||||||
[Parameter(Position=0)]
|
[Parameter(Position=0)]
|
||||||
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','q')]
|
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','kiro-cli','agy','bob','qodercli','vibe','kimi','generic')]
|
||||||
[string]$AgentType
|
[string]$AgentType
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ $NEW_PLAN = $IMPL_PLAN
|
|||||||
# Agent file paths
|
# Agent file paths
|
||||||
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
|
$CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
|
||||||
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
|
$GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
|
||||||
$COPILOT_FILE = Join-Path $REPO_ROOT '.github/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'
|
$CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc'
|
||||||
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
|
$QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
|
||||||
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
$AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||||
@@ -55,8 +55,15 @@ $KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/rules/specify-rules.md'
|
|||||||
$AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md'
|
$AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md'
|
||||||
$ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
|
$ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
|
||||||
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
|
$CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
|
||||||
|
$QODER_FILE = Join-Path $REPO_ROOT 'QODER.md'
|
||||||
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
$AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||||
$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
$SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md'
|
||||||
|
$TABNINE_FILE = Join-Path $REPO_ROOT 'TABNINE.md'
|
||||||
|
$KIRO_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||||
|
$AGY_FILE = Join-Path $REPO_ROOT '.agent/rules/specify-rules.md'
|
||||||
|
$BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
|
||||||
|
$VIBE_FILE = Join-Path $REPO_ROOT '.vibe/agents/specify-agents.md'
|
||||||
|
$KIMI_FILE = Join-Path $REPO_ROOT 'KIMI.md'
|
||||||
|
|
||||||
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
|
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
|
||||||
|
|
||||||
@@ -254,6 +261,12 @@ function New-AgentFile {
|
|||||||
# Convert literal \n sequences introduced by Escape to real newlines
|
# Convert literal \n sequences introduced by Escape to real newlines
|
||||||
$content = $content -replace '\\n',[Environment]::NewLine
|
$content = $content -replace '\\n',[Environment]::NewLine
|
||||||
|
|
||||||
|
# Prepend Cursor frontmatter for .mdc files so rules are auto-included
|
||||||
|
if ($TargetFile -match '\.mdc$') {
|
||||||
|
$frontmatter = @('---','description: Project Development Guidelines','globs: ["**/*"]','alwaysApply: true','---','') -join [Environment]::NewLine
|
||||||
|
$content = $frontmatter + $content
|
||||||
|
}
|
||||||
|
|
||||||
$parent = Split-Path -Parent $TargetFile
|
$parent = Split-Path -Parent $TargetFile
|
||||||
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
|
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
|
||||||
Set-Content -LiteralPath $TargetFile -Value $content -NoNewline -Encoding utf8
|
Set-Content -LiteralPath $TargetFile -Value $content -NoNewline -Encoding utf8
|
||||||
@@ -318,7 +331,7 @@ function Update-ExistingAgentFile {
|
|||||||
if ($existingChanges -lt 2) { $output.Add($line); $existingChanges++ }
|
if ($existingChanges -lt 2) { $output.Add($line); $existingChanges++ }
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ($line -match '\*\*Last updated\*\*: .*\d{4}-\d{2}-\d{2}') {
|
if ($line -match '(\*\*)?Last updated(\*\*)?: .*\d{4}-\d{2}-\d{2}') {
|
||||||
$output.Add(($line -replace '\d{4}-\d{2}-\d{2}',$Date.ToString('yyyy-MM-dd')))
|
$output.Add(($line -replace '\d{4}-\d{2}-\d{2}',$Date.ToString('yyyy-MM-dd')))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -330,6 +343,12 @@ function Update-ExistingAgentFile {
|
|||||||
$newTechEntries | ForEach-Object { $output.Add($_) }
|
$newTechEntries | ForEach-Object { $output.Add($_) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ensure Cursor .mdc files have YAML frontmatter for auto-inclusion
|
||||||
|
if ($TargetFile -match '\.mdc$' -and $output.Count -gt 0 -and $output[0] -ne '---') {
|
||||||
|
$frontmatter = @('---','description: Project Development Guidelines','globs: ["**/*"]','alwaysApply: true','---','')
|
||||||
|
$output.InsertRange(0, $frontmatter)
|
||||||
|
}
|
||||||
|
|
||||||
Set-Content -LiteralPath $TargetFile -Value ($output -join [Environment]::NewLine) -Encoding utf8
|
Set-Content -LiteralPath $TargetFile -Value ($output -join [Environment]::NewLine) -Encoding utf8
|
||||||
return $true
|
return $true
|
||||||
}
|
}
|
||||||
@@ -380,9 +399,17 @@ function Update-SpecificAgent {
|
|||||||
'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
|
'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
|
||||||
'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
|
'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
|
||||||
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
|
'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
|
||||||
|
'qodercli' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' }
|
||||||
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
|
'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
|
||||||
'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' }
|
'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' }
|
||||||
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q'; return $false }
|
'tabnine' { Update-AgentFile -TargetFile $TABNINE_FILE -AgentName 'Tabnine CLI' }
|
||||||
|
'kiro-cli' { Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI' }
|
||||||
|
'agy' { Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity' }
|
||||||
|
'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' }
|
||||||
|
'vibe' { Update-AgentFile -TargetFile $VIBE_FILE -AgentName 'Mistral Vibe' }
|
||||||
|
'kimi' { Update-AgentFile -TargetFile $KIMI_FILE -AgentName 'Kimi Code' }
|
||||||
|
'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' }
|
||||||
|
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|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|generic'; return $false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +427,14 @@ function Update-AllExistingAgents {
|
|||||||
if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true }
|
if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true }
|
||||||
if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true }
|
if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true }
|
||||||
if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true }
|
if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true }
|
||||||
if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer 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 $TABNINE_FILE) { if (-not (Update-AgentFile -TargetFile $TABNINE_FILE -AgentName 'Tabnine CLI')) { $ok = $false }; $found = $true }
|
||||||
|
if (Test-Path $KIRO_FILE) { if (-not (Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI')) { $ok = $false }; $found = $true }
|
||||||
|
if (Test-Path $AGY_FILE) { if (-not (Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity')) { $ok = $false }; $found = $true }
|
||||||
|
if (Test-Path $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true }
|
||||||
|
if (Test-Path $VIBE_FILE) { if (-not (Update-AgentFile -TargetFile $VIBE_FILE -AgentName 'Mistral Vibe')) { $ok = $false }; $found = $true }
|
||||||
|
if (Test-Path $KIMI_FILE) { if (-not (Update-AgentFile -TargetFile $KIMI_FILE -AgentName 'Kimi Code')) { $ok = $false }; $found = $true }
|
||||||
if (-not $found) {
|
if (-not $found) {
|
||||||
Write-Info 'No existing agent files found, creating default Claude file...'
|
Write-Info 'No existing agent files found, creating default Claude file...'
|
||||||
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
|
if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
|
||||||
@@ -415,7 +449,7 @@ function Print-Summary {
|
|||||||
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
|
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
|
||||||
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
|
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
|
||||||
Write-Host ''
|
Write-Host ''
|
||||||
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|q]'
|
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic]'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Main {
|
function Main {
|
||||||
@@ -436,4 +470,3 @@ function Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Main
|
Main
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ The templates include comprehensive checklists that act as "unit tests" for the
|
|||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
### Requirement Completeness
|
### Requirement Completeness
|
||||||
|
|
||||||
- [ ] No [NEEDS CLARIFICATION] markers remain
|
- [ ] No [NEEDS CLARIFICATION] markers remain
|
||||||
- [ ] Requirements are testable and unambiguous
|
- [ ] Requirements are testable and unambiguous
|
||||||
- [ ] Success criteria are measurable
|
- [ ] Success criteria are measurable
|
||||||
@@ -208,10 +209,14 @@ The implementation plan template enforces architectural principles through phase
|
|||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
### Phase -1: Pre-Implementation Gates
|
### Phase -1: Pre-Implementation Gates
|
||||||
|
|
||||||
#### Simplicity Gate (Article VII)
|
#### Simplicity Gate (Article VII)
|
||||||
|
|
||||||
- [ ] Using ≤3 projects?
|
- [ ] Using ≤3 projects?
|
||||||
- [ ] No future-proofing?
|
- [ ] No future-proofing?
|
||||||
|
|
||||||
#### Anti-Abstraction Gate (Article VIII)
|
#### Anti-Abstraction Gate (Article VIII)
|
||||||
|
|
||||||
- [ ] Using framework directly?
|
- [ ] Using framework directly?
|
||||||
- [ ] Single model representation?
|
- [ ] Single model representation?
|
||||||
```
|
```
|
||||||
@@ -347,15 +352,19 @@ The implementation plan template operationalizes these articles through concrete
|
|||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
### Phase -1: Pre-Implementation Gates
|
### Phase -1: Pre-Implementation Gates
|
||||||
|
|
||||||
#### Simplicity Gate (Article VII)
|
#### Simplicity Gate (Article VII)
|
||||||
|
|
||||||
- [ ] Using ≤3 projects?
|
- [ ] Using ≤3 projects?
|
||||||
- [ ] No future-proofing?
|
- [ ] No future-proofing?
|
||||||
|
|
||||||
#### Anti-Abstraction Gate (Article VIII)
|
#### Anti-Abstraction Gate (Article VIII)
|
||||||
|
|
||||||
- [ ] Using framework directly?
|
- [ ] Using framework directly?
|
||||||
- [ ] Single model representation?
|
- [ ] Single model representation?
|
||||||
|
|
||||||
#### Integration-First Gate (Article IX)
|
#### Integration-First Gate (Article IX)
|
||||||
|
|
||||||
- [ ] Contracts defined?
|
- [ ] Contracts defined?
|
||||||
- [ ] Contract tests written?
|
- [ ] Contract tests written?
|
||||||
```
|
```
|
||||||
|
|||||||
8
spec-kit.code-workspace
Normal file
8
spec-kit.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
422
src/specify_cli/agents.py
Normal file
422
src/specify_cli/agents.py
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
"""
|
||||||
|
Agent Command Registrar for Spec Kit
|
||||||
|
|
||||||
|
Shared infrastructure for registering commands with AI agents.
|
||||||
|
Used by both the extension system and the preset system to write
|
||||||
|
command files into agent-specific directories in the correct format.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
class CommandRegistrar:
|
||||||
|
"""Handles registration of commands with AI agents.
|
||||||
|
|
||||||
|
Supports writing command files in Markdown or TOML format to the
|
||||||
|
appropriate agent directory, with correct argument placeholders
|
||||||
|
and companion files (e.g. Copilot .prompt.md).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Agent configurations with directory, format, and argument placeholder
|
||||||
|
AGENT_CONFIGS = {
|
||||||
|
"claude": {
|
||||||
|
"dir": ".claude/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"gemini": {
|
||||||
|
"dir": ".gemini/commands",
|
||||||
|
"format": "toml",
|
||||||
|
"args": "{{args}}",
|
||||||
|
"extension": ".toml"
|
||||||
|
},
|
||||||
|
"copilot": {
|
||||||
|
"dir": ".github/agents",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".agent.md"
|
||||||
|
},
|
||||||
|
"cursor": {
|
||||||
|
"dir": ".cursor/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"qwen": {
|
||||||
|
"dir": ".qwen/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"opencode": {
|
||||||
|
"dir": ".opencode/command",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"codex": {
|
||||||
|
"dir": ".codex/prompts",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"windsurf": {
|
||||||
|
"dir": ".windsurf/workflows",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"kilocode": {
|
||||||
|
"dir": ".kilocode/workflows",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"auggie": {
|
||||||
|
"dir": ".augment/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"roo": {
|
||||||
|
"dir": ".roo/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"codebuddy": {
|
||||||
|
"dir": ".codebuddy/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"qodercli": {
|
||||||
|
"dir": ".qoder/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"kiro-cli": {
|
||||||
|
"dir": ".kiro/prompts",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"amp": {
|
||||||
|
"dir": ".agents/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"shai": {
|
||||||
|
"dir": ".shai/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"tabnine": {
|
||||||
|
"dir": ".tabnine/agent/commands",
|
||||||
|
"format": "toml",
|
||||||
|
"args": "{{args}}",
|
||||||
|
"extension": ".toml"
|
||||||
|
},
|
||||||
|
"bob": {
|
||||||
|
"dir": ".bob/commands",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": ".md"
|
||||||
|
},
|
||||||
|
"kimi": {
|
||||||
|
"dir": ".kimi/skills",
|
||||||
|
"format": "markdown",
|
||||||
|
"args": "$ARGUMENTS",
|
||||||
|
"extension": "/SKILL.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_frontmatter(content: str) -> tuple[dict, str]:
|
||||||
|
"""Parse YAML frontmatter from Markdown content.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Markdown content with YAML frontmatter
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple of (frontmatter_dict, body_content)
|
||||||
|
"""
|
||||||
|
if not content.startswith("---"):
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
# Find second ---
|
||||||
|
end_marker = content.find("---", 3)
|
||||||
|
if end_marker == -1:
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
frontmatter_str = content[3:end_marker].strip()
|
||||||
|
body = content[end_marker + 3:].strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
frontmatter = yaml.safe_load(frontmatter_str) or {}
|
||||||
|
except yaml.YAMLError:
|
||||||
|
frontmatter = {}
|
||||||
|
|
||||||
|
return frontmatter, body
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def render_frontmatter(fm: dict) -> str:
|
||||||
|
"""Render frontmatter dictionary as YAML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fm: Frontmatter dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
YAML-formatted frontmatter with delimiters
|
||||||
|
"""
|
||||||
|
if not fm:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
yaml_str = yaml.dump(fm, default_flow_style=False, sort_keys=False)
|
||||||
|
return f"---\n{yaml_str}---\n"
|
||||||
|
|
||||||
|
def _adjust_script_paths(self, frontmatter: dict) -> dict:
|
||||||
|
"""Adjust script paths from extension-relative to repo-relative.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frontmatter: Frontmatter dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Modified frontmatter with adjusted paths
|
||||||
|
"""
|
||||||
|
if "scripts" in frontmatter:
|
||||||
|
for key in frontmatter["scripts"]:
|
||||||
|
script_path = frontmatter["scripts"][key]
|
||||||
|
if script_path.startswith("../../scripts/"):
|
||||||
|
frontmatter["scripts"][key] = f".specify/scripts/{script_path[14:]}"
|
||||||
|
return frontmatter
|
||||||
|
|
||||||
|
def render_markdown_command(
|
||||||
|
self,
|
||||||
|
frontmatter: dict,
|
||||||
|
body: str,
|
||||||
|
source_id: str,
|
||||||
|
context_note: str = None
|
||||||
|
) -> str:
|
||||||
|
"""Render command in Markdown format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frontmatter: Command frontmatter
|
||||||
|
body: Command body content
|
||||||
|
source_id: Source identifier (extension or preset ID)
|
||||||
|
context_note: Custom context comment (default: <!-- Source: {source_id} -->)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted Markdown command file content
|
||||||
|
"""
|
||||||
|
if context_note is None:
|
||||||
|
context_note = f"\n<!-- Source: {source_id} -->\n"
|
||||||
|
return self.render_frontmatter(frontmatter) + "\n" + context_note + body
|
||||||
|
|
||||||
|
def render_toml_command(
|
||||||
|
self,
|
||||||
|
frontmatter: dict,
|
||||||
|
body: str,
|
||||||
|
source_id: str
|
||||||
|
) -> str:
|
||||||
|
"""Render command in TOML format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frontmatter: Command frontmatter
|
||||||
|
body: Command body content
|
||||||
|
source_id: Source identifier (extension or preset ID)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted TOML command file content
|
||||||
|
"""
|
||||||
|
toml_lines = []
|
||||||
|
|
||||||
|
if "description" in frontmatter:
|
||||||
|
desc = frontmatter["description"].replace('"', '\\"')
|
||||||
|
toml_lines.append(f'description = "{desc}"')
|
||||||
|
toml_lines.append("")
|
||||||
|
|
||||||
|
toml_lines.append(f"# Source: {source_id}")
|
||||||
|
toml_lines.append("")
|
||||||
|
|
||||||
|
toml_lines.append('prompt = """')
|
||||||
|
toml_lines.append(body)
|
||||||
|
toml_lines.append('"""')
|
||||||
|
|
||||||
|
return "\n".join(toml_lines)
|
||||||
|
|
||||||
|
def _convert_argument_placeholder(self, content: str, from_placeholder: str, to_placeholder: str) -> str:
|
||||||
|
"""Convert argument placeholder format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: Command content
|
||||||
|
from_placeholder: Source placeholder (e.g., "$ARGUMENTS")
|
||||||
|
to_placeholder: Target placeholder (e.g., "{{args}}")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Content with converted placeholders
|
||||||
|
"""
|
||||||
|
return content.replace(from_placeholder, to_placeholder)
|
||||||
|
|
||||||
|
def register_commands(
|
||||||
|
self,
|
||||||
|
agent_name: str,
|
||||||
|
commands: List[Dict[str, Any]],
|
||||||
|
source_id: str,
|
||||||
|
source_dir: Path,
|
||||||
|
project_root: Path,
|
||||||
|
context_note: str = None
|
||||||
|
) -> List[str]:
|
||||||
|
"""Register commands for a specific agent.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_name: Agent name (claude, gemini, copilot, etc.)
|
||||||
|
commands: List of command info dicts with 'name', 'file', and optional 'aliases'
|
||||||
|
source_id: Identifier of the source (extension or preset ID)
|
||||||
|
source_dir: Directory containing command source files
|
||||||
|
project_root: Path to project root
|
||||||
|
context_note: Custom context comment for markdown output
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of registered command names
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If agent is not supported
|
||||||
|
"""
|
||||||
|
if agent_name not in self.AGENT_CONFIGS:
|
||||||
|
raise ValueError(f"Unsupported agent: {agent_name}")
|
||||||
|
|
||||||
|
agent_config = self.AGENT_CONFIGS[agent_name]
|
||||||
|
commands_dir = project_root / agent_config["dir"]
|
||||||
|
commands_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
registered = []
|
||||||
|
|
||||||
|
for cmd_info in commands:
|
||||||
|
cmd_name = cmd_info["name"]
|
||||||
|
cmd_file = cmd_info["file"]
|
||||||
|
|
||||||
|
source_file = source_dir / cmd_file
|
||||||
|
if not source_file.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
content = source_file.read_text(encoding="utf-8")
|
||||||
|
frontmatter, body = self.parse_frontmatter(content)
|
||||||
|
|
||||||
|
frontmatter = self._adjust_script_paths(frontmatter)
|
||||||
|
|
||||||
|
body = self._convert_argument_placeholder(
|
||||||
|
body, "$ARGUMENTS", agent_config["args"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if agent_config["format"] == "markdown":
|
||||||
|
output = self.render_markdown_command(frontmatter, body, source_id, context_note)
|
||||||
|
elif agent_config["format"] == "toml":
|
||||||
|
output = self.render_toml_command(frontmatter, body, source_id)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported format: {agent_config['format']}")
|
||||||
|
|
||||||
|
dest_file = commands_dir / f"{cmd_name}{agent_config['extension']}"
|
||||||
|
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
dest_file.write_text(output, encoding="utf-8")
|
||||||
|
|
||||||
|
if agent_name == "copilot":
|
||||||
|
self.write_copilot_prompt(project_root, cmd_name)
|
||||||
|
|
||||||
|
registered.append(cmd_name)
|
||||||
|
|
||||||
|
for alias in cmd_info.get("aliases", []):
|
||||||
|
alias_file = commands_dir / f"{alias}{agent_config['extension']}"
|
||||||
|
alias_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
alias_file.write_text(output, encoding="utf-8")
|
||||||
|
if agent_name == "copilot":
|
||||||
|
self.write_copilot_prompt(project_root, alias)
|
||||||
|
registered.append(alias)
|
||||||
|
|
||||||
|
return registered
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def write_copilot_prompt(project_root: Path, cmd_name: str) -> None:
|
||||||
|
"""Generate a companion .prompt.md file for a Copilot agent command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
project_root: Path to project root
|
||||||
|
cmd_name: Command name (e.g. 'speckit.my-ext.example')
|
||||||
|
"""
|
||||||
|
prompts_dir = project_root / ".github" / "prompts"
|
||||||
|
prompts_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
prompt_file = prompts_dir / f"{cmd_name}.prompt.md"
|
||||||
|
prompt_file.write_text(f"---\nagent: {cmd_name}\n---\n", encoding="utf-8")
|
||||||
|
|
||||||
|
def register_commands_for_all_agents(
|
||||||
|
self,
|
||||||
|
commands: List[Dict[str, Any]],
|
||||||
|
source_id: str,
|
||||||
|
source_dir: Path,
|
||||||
|
project_root: Path,
|
||||||
|
context_note: str = None
|
||||||
|
) -> Dict[str, List[str]]:
|
||||||
|
"""Register commands for all detected agents in the project.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
commands: List of command info dicts
|
||||||
|
source_id: Identifier of the source (extension or preset ID)
|
||||||
|
source_dir: Directory containing command source files
|
||||||
|
project_root: Path to project root
|
||||||
|
context_note: Custom context comment for markdown output
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary mapping agent names to list of registered commands
|
||||||
|
"""
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
for agent_name, agent_config in self.AGENT_CONFIGS.items():
|
||||||
|
agent_dir = project_root / agent_config["dir"].split("/")[0]
|
||||||
|
|
||||||
|
if agent_dir.exists():
|
||||||
|
try:
|
||||||
|
registered = self.register_commands(
|
||||||
|
agent_name, commands, source_id, source_dir, project_root,
|
||||||
|
context_note=context_note
|
||||||
|
)
|
||||||
|
if registered:
|
||||||
|
results[agent_name] = registered
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def unregister_commands(
|
||||||
|
self,
|
||||||
|
registered_commands: Dict[str, List[str]],
|
||||||
|
project_root: Path
|
||||||
|
) -> None:
|
||||||
|
"""Remove previously registered command files from agent directories.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
registered_commands: Dict mapping agent names to command name lists
|
||||||
|
project_root: Path to project root
|
||||||
|
"""
|
||||||
|
for agent_name, cmd_names in registered_commands.items():
|
||||||
|
if agent_name not in self.AGENT_CONFIGS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
agent_config = self.AGENT_CONFIGS[agent_name]
|
||||||
|
commands_dir = project_root / agent_config["dir"]
|
||||||
|
|
||||||
|
for cmd_name in cmd_names:
|
||||||
|
cmd_file = commands_dir / f"{cmd_name}{agent_config['extension']}"
|
||||||
|
if cmd_file.exists():
|
||||||
|
cmd_file.unlink()
|
||||||
|
|
||||||
|
if agent_name == "copilot":
|
||||||
|
prompt_file = project_root / ".github" / "prompts" / f"{cmd_name}.prompt.md"
|
||||||
|
if prompt_file.exists():
|
||||||
|
prompt_file.unlink()
|
||||||
1884
src/specify_cli/extensions.py
Normal file
1884
src/specify_cli/extensions.py
Normal file
File diff suppressed because it is too large
Load Diff
1530
src/specify_cli/presets.py
Normal file
1530
src/specify_cli/presets.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -94,9 +94,10 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
- Generate unique checklist filename:
|
- Generate unique checklist filename:
|
||||||
- Use short, descriptive name based on domain (e.g., `ux.md`, `api.md`, `security.md`)
|
- 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
|
- File handling behavior:
|
||||||
- Number items sequentially starting from CHK001
|
- If file does NOT exist: Create new file and number items starting from CHK001
|
||||||
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists)
|
- If file exists: Append new items to existing file, continuing from the last CHK ID (e.g., if last item is CHK015, start new items at CHK016)
|
||||||
|
- Never delete or replace existing checklist content - always preserve and append
|
||||||
|
|
||||||
**CORE PRINCIPLE - Test the Requirements, Not the Implementation**:
|
**CORE PRINCIPLE - Test the Requirements, Not the Implementation**:
|
||||||
Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for:
|
Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for:
|
||||||
@@ -208,13 +209,13 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
|
|
||||||
6. **Structure Reference**: Generate the checklist following the canonical template in `templates/checklist-template.md` for title, meta section, category headings, and ID formatting. If template is unavailable, use: H1 title, purpose/created meta lines, `##` category sections containing `- [ ] CHK### <requirement item>` lines with globally incrementing IDs starting at CHK001.
|
6. **Structure Reference**: Generate the checklist following the canonical template in `templates/checklist-template.md` for title, meta section, category headings, and ID formatting. If template is unavailable, use: H1 title, purpose/created meta lines, `##` category sections containing `- [ ] CHK### <requirement item>` lines with globally incrementing IDs starting at CHK001.
|
||||||
|
|
||||||
7. **Report**: Output full path to created checklist, item count, and remind user that each run creates a new file. Summarize:
|
7. **Report**: Output full path to checklist file, item count, and summarize whether the run created a new file or appended to an existing one. Summarize:
|
||||||
- Focus areas selected
|
- Focus areas selected
|
||||||
- Depth level
|
- Depth level
|
||||||
- Actor/timing
|
- Actor/timing
|
||||||
- Any explicit user-specified must-have items incorporated
|
- Any explicit user-specified must-have items incorporated
|
||||||
|
|
||||||
**Important**: Each `/speckit.checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows:
|
**Important**: Each `/speckit.checklist` command invocation uses a short, descriptive checklist filename and either creates a new file or appends to an existing one. This allows:
|
||||||
|
|
||||||
- Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`)
|
- Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`)
|
||||||
- Simple, memorable filenames that indicate checklist purpose
|
- Simple, memorable filenames that indicate checklist purpose
|
||||||
|
|||||||
@@ -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.
|
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:
|
scripts:
|
||||||
sh: scripts/bash/check-prerequisites.sh --json --paths-only
|
sh: scripts/bash/check-prerequisites.sh --json --paths-only
|
||||||
ps: scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly
|
ps: scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly
|
||||||
@@ -85,7 +89,7 @@ Execution steps:
|
|||||||
- Information is better deferred to planning phase (note internally)
|
- Information is better deferred to planning phase (note internally)
|
||||||
|
|
||||||
3. Generate (internally) a prioritized queue of candidate clarification questions (maximum 5). Do NOT output them all at once. Apply these constraints:
|
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.
|
- Maximum of 5 total questions across the whole session.
|
||||||
- Each question must be answerable with EITHER:
|
- Each question must be answerable with EITHER:
|
||||||
- A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
|
- A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
|
||||||
- A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").
|
- A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
---
|
---
|
||||||
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync
|
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
|
||||||
|
handoffs:
|
||||||
|
- label: Build Specification
|
||||||
|
agent: speckit.specify
|
||||||
|
prompt: Implement the feature specification based on the updated constitution. I want to build...
|
||||||
---
|
---
|
||||||
|
|
||||||
## User Input
|
## User Input
|
||||||
@@ -12,11 +16,13 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
|
|
||||||
## Outline
|
## Outline
|
||||||
|
|
||||||
You are updating the project constitution at `/memory/constitution.md`. This file is a TEMPLATE containing placeholder tokens in square brackets (e.g. `[PROJECT_NAME]`, `[PRINCIPLE_1_NAME]`). Your job is to (a) collect/derive concrete values, (b) fill the template precisely, and (c) propagate any amendments across dependent artifacts.
|
You are updating the project constitution at `.specify/memory/constitution.md`. This file is a TEMPLATE containing placeholder tokens in square brackets (e.g. `[PROJECT_NAME]`, `[PRINCIPLE_1_NAME]`). Your job is to (a) collect/derive concrete values, (b) fill the template precisely, and (c) propagate any amendments across dependent artifacts.
|
||||||
|
|
||||||
|
**Note**: If `.specify/memory/constitution.md` does not exist yet, it should have been initialized from `.specify/templates/constitution-template.md` during project setup. If it's missing, copy the template first.
|
||||||
|
|
||||||
Follow this execution flow:
|
Follow this execution flow:
|
||||||
|
|
||||||
1. Load the existing constitution template at `/memory/constitution.md`.
|
1. Load the existing constitution at `.specify/memory/constitution.md`.
|
||||||
- Identify every placeholder token of the form `[ALL_CAPS_IDENTIFIER]`.
|
- Identify every placeholder token of the form `[ALL_CAPS_IDENTIFIER]`.
|
||||||
**IMPORTANT**: The user might require less or more principles than the ones used in the template. If a number is specified, respect that - follow the general template. You will update the doc accordingly.
|
**IMPORTANT**: The user might require less or more principles than the ones used in the template. If a number is specified, respect that - follow the general template. You will update the doc accordingly.
|
||||||
|
|
||||||
@@ -37,10 +43,10 @@ Follow this execution flow:
|
|||||||
- Ensure Governance section lists amendment procedure, versioning policy, and compliance review expectations.
|
- Ensure Governance section lists amendment procedure, versioning policy, and compliance review expectations.
|
||||||
|
|
||||||
4. Consistency propagation checklist (convert prior checklist into active validations):
|
4. Consistency propagation checklist (convert prior checklist into active validations):
|
||||||
- Read `/templates/plan-template.md` and ensure any "Constitution Check" or rules align with updated principles.
|
- Read `.specify/templates/plan-template.md` and ensure any "Constitution Check" or rules align with updated principles.
|
||||||
- Read `/templates/spec-template.md` for scope/requirements alignment—update if constitution adds/removes mandatory sections or constraints.
|
- Read `.specify/templates/spec-template.md` for scope/requirements alignment—update if constitution adds/removes mandatory sections or constraints.
|
||||||
- Read `/templates/tasks-template.md` and ensure task categorization reflects new or removed principle-driven task types (e.g., observability, versioning, testing discipline).
|
- Read `.specify/templates/tasks-template.md` and ensure task categorization reflects new or removed principle-driven task types (e.g., observability, versioning, testing discipline).
|
||||||
- Read each command file in `/templates/commands/*.md` (including this one) to verify no outdated references (agent-specific names like CLAUDE only) remain when generic guidance is required.
|
- Read each command file in `.specify/templates/commands/*.md` (including this one) to verify no outdated references (agent-specific names like CLAUDE only) remain when generic guidance is required.
|
||||||
- Read any runtime guidance docs (e.g., `README.md`, `docs/quickstart.md`, or agent-specific guidance files if present). Update references to principles changed.
|
- Read any runtime guidance docs (e.g., `README.md`, `docs/quickstart.md`, or agent-specific guidance files if present). Update references to principles changed.
|
||||||
|
|
||||||
5. Produce a Sync Impact Report (prepend as an HTML comment at top of the constitution file after update):
|
5. Produce a Sync Impact Report (prepend as an HTML comment at top of the constitution file after update):
|
||||||
@@ -57,7 +63,7 @@ Follow this execution flow:
|
|||||||
- Dates ISO format YYYY-MM-DD.
|
- Dates ISO format YYYY-MM-DD.
|
||||||
- Principles are declarative, testable, and free of vague language ("should" → replace with MUST/SHOULD rationale where appropriate).
|
- Principles are declarative, testable, and free of vague language ("should" → replace with MUST/SHOULD rationale where appropriate).
|
||||||
|
|
||||||
7. Write the completed constitution back to `/memory/constitution.md` (overwrite).
|
7. Write the completed constitution back to `.specify/memory/constitution.md` (overwrite).
|
||||||
|
|
||||||
8. Output a final summary to the user with:
|
8. Output a final summary to the user with:
|
||||||
- New version and bump rationale.
|
- New version and bump rationale.
|
||||||
@@ -75,4 +81,4 @@ If the user supplies partial updates (e.g., only one principle revision), still
|
|||||||
|
|
||||||
If critical info missing (e.g., ratification date truly unknown), insert `TODO(<FIELD_NAME>): explanation` and include in the Sync Impact Report under deferred items.
|
If critical info missing (e.g., ratification date truly unknown), insert `TODO(<FIELD_NAME>): explanation` and include in the Sync Impact Report under deferred items.
|
||||||
|
|
||||||
Do not create a new template; always operate on the existing `/memory/constitution.md` file.
|
Do not create a new template; always operate on the existing `.specify/memory/constitution.md` file.
|
||||||
|
|||||||
@@ -13,6 +13,40 @@ $ARGUMENTS
|
|||||||
|
|
||||||
You **MUST** consider the user input before proceeding (if not empty).
|
You **MUST** consider the user input before proceeding (if not empty).
|
||||||
|
|
||||||
|
## Pre-Execution Checks
|
||||||
|
|
||||||
|
**Check for extension hooks (before implementation)**:
|
||||||
|
- Check if `.specify/extensions.yml` exists in the project root.
|
||||||
|
- If it exists, read it and look for entries under the `hooks.before_implement` key
|
||||||
|
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||||
|
- Filter to only hooks where `enabled: true`
|
||||||
|
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||||
|
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||||
|
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||||
|
- For each executable hook, output the following based on its `optional` flag:
|
||||||
|
- **Optional hook** (`optional: true`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Pre-Hook**: {extension}
|
||||||
|
Command: `/{command}`
|
||||||
|
Description: {description}
|
||||||
|
|
||||||
|
Prompt: {prompt}
|
||||||
|
To execute: `/{command}`
|
||||||
|
```
|
||||||
|
- **Mandatory hook** (`optional: false`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Automatic Pre-Hook**: {extension}
|
||||||
|
Executing: `/{command}`
|
||||||
|
EXECUTE_COMMAND: {command}
|
||||||
|
|
||||||
|
Wait for the result of the hook command before proceeding to the Outline.
|
||||||
|
```
|
||||||
|
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||||
|
|
||||||
## Outline
|
## 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. 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").
|
||||||
@@ -67,7 +101,8 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
```
|
```
|
||||||
|
|
||||||
- Check if Dockerfile* exists or Docker in plan.md → create/verify .dockerignore
|
- Check if Dockerfile* exists or Docker in plan.md → create/verify .dockerignore
|
||||||
- Check if .eslintrc*or eslint.config.* exists → create/verify .eslintignore
|
- 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 .prettierrc* exists → create/verify .prettierignore
|
||||||
- Check if .npmrc or package.json exists → create/verify .npmignore (if publishing)
|
- Check if .npmrc or package.json exists → create/verify .npmignore (if publishing)
|
||||||
- Check if terraform files (*.tf) exist → create/verify .terraformignore
|
- Check if terraform files (*.tf) exist → create/verify .terraformignore
|
||||||
@@ -87,7 +122,7 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
- **Rust**: `target/`, `debug/`, `release/`, `*.rs.bk`, `*.rlib`, `*.prof*`, `.idea/`, `*.log`, `.env*`
|
- **Rust**: `target/`, `debug/`, `release/`, `*.rs.bk`, `*.rlib`, `*.prof*`, `.idea/`, `*.log`, `.env*`
|
||||||
- **Kotlin**: `build/`, `out/`, `.gradle/`, `.idea/`, `*.class`, `*.jar`, `*.iml`, `*.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`, `*.so`, `*.a`, `*.exe`, `*.dll`, `.idea/`, `*.log`, `.env*`
|
||||||
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `Makefile`, `config.log`, `.idea/`, `*.log`, `.env*`
|
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `*.dll`, `autom4te.cache/`, `config.status`, `config.log`, `.idea/`, `*.log`, `.env*`
|
||||||
- **Swift**: `.build/`, `DerivedData/`, `*.swiftpm/`, `Packages/`
|
- **Swift**: `.build/`, `DerivedData/`, `*.swiftpm/`, `Packages/`
|
||||||
- **R**: `.Rproj.user/`, `.Rhistory`, `.RData`, `.Ruserdata`, `*.Rproj`, `packrat/`, `renv/`
|
- **R**: `.Rproj.user/`, `.Rhistory`, `.RData`, `.Ruserdata`, `*.Rproj`, `packrat/`, `renv/`
|
||||||
- **Universal**: `.DS_Store`, `Thumbs.db`, `*.tmp`, `*.swp`, `.vscode/`, `.idea/`
|
- **Universal**: `.DS_Store`, `Thumbs.db`, `*.tmp`, `*.swp`, `.vscode/`, `.idea/`
|
||||||
@@ -136,3 +171,31 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
10. **Check for extension hooks**: After completion validation, check if `.specify/extensions.yml` exists in the project root.
|
||||||
|
- If it exists, read it and look for entries under the `hooks.after_implement` key
|
||||||
|
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||||
|
- Filter to only hooks where `enabled: true`
|
||||||
|
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||||
|
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||||
|
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||||
|
- For each executable hook, output the following based on its `optional` flag:
|
||||||
|
- **Optional hook** (`optional: true`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Hook**: {extension}
|
||||||
|
Command: `/{command}`
|
||||||
|
Description: {description}
|
||||||
|
|
||||||
|
Prompt: {prompt}
|
||||||
|
To execute: `/{command}`
|
||||||
|
```
|
||||||
|
- **Mandatory hook** (`optional: false`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Automatic Hook**: {extension}
|
||||||
|
Executing: `/{command}`
|
||||||
|
EXECUTE_COMMAND: {command}
|
||||||
|
```
|
||||||
|
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
---
|
---
|
||||||
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
|
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:
|
scripts:
|
||||||
sh: scripts/bash/setup-plan.sh --json
|
sh: scripts/bash/setup-plan.sh --json
|
||||||
ps: scripts/powershell/setup-plan.ps1 -Json
|
ps: scripts/powershell/setup-plan.ps1 -Json
|
||||||
@@ -67,10 +75,11 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
- Validation rules from requirements
|
- Validation rules from requirements
|
||||||
- State transitions if applicable
|
- State transitions if applicable
|
||||||
|
|
||||||
2. **Generate API contracts** from functional requirements:
|
2. **Define interface contracts** (if project has external interfaces) → `/contracts/`:
|
||||||
- For each user action → endpoint
|
- Identify what interfaces the project exposes to users or other systems
|
||||||
- Use standard REST/GraphQL patterns
|
- Document the contract format appropriate for the project type
|
||||||
- Output OpenAPI/GraphQL schema to `/contracts/`
|
- Examples: public APIs for libraries, command schemas for CLI tools, endpoints for web services, grammars for parsers, UI contracts for applications
|
||||||
|
- Skip if project is purely internal (build scripts, one-off tools, etc.)
|
||||||
|
|
||||||
3. **Agent context update**:
|
3. **Agent context update**:
|
||||||
- Run `{AGENT_SCRIPT}`
|
- Run `{AGENT_SCRIPT}`
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
---
|
---
|
||||||
description: Create or update the feature specification from a natural language feature description.
|
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:
|
scripts:
|
||||||
sh: scripts/bash/create-new-feature.sh --json "{ARGS}"
|
sh: scripts/bash/create-new-feature.sh "{ARGS}"
|
||||||
ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"
|
ps: scripts/powershell/create-new-feature.ps1 "{ARGS}"
|
||||||
---
|
---
|
||||||
|
|
||||||
## User Input
|
## User Input
|
||||||
@@ -31,16 +39,18 @@ Given that feature description, do this:
|
|||||||
- "Create a dashboard for analytics" → "analytics-dashboard"
|
- "Create a dashboard for analytics" → "analytics-dashboard"
|
||||||
- "Fix payment processing timeout bug" → "fix-payment-timeout"
|
- "Fix payment processing timeout bug" → "fix-payment-timeout"
|
||||||
|
|
||||||
2. Run the script `{SCRIPT}` from repo root **with the short-name argument** and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
|
2. **Create the feature branch** by running the script with `--short-name` (and `--json`), and do NOT pass `--number` (the script auto-detects the next globally available number across all branches and spec directories):
|
||||||
|
|
||||||
|
- Bash example: `{SCRIPT} --json --short-name "user-auth" "Add user authentication"`
|
||||||
|
- PowerShell example: `{SCRIPT} -Json -ShortName "user-auth" "Add user authentication"`
|
||||||
|
|
||||||
**IMPORTANT**:
|
**IMPORTANT**:
|
||||||
|
- Do NOT pass `--number` — the script determines the correct next number automatically
|
||||||
- Append the short-name argument to the `{SCRIPT}` command with the 2-4 word short name you created in step 1
|
- Always include the JSON flag (`--json` for Bash, `-Json` for PowerShell) so the output can be parsed reliably
|
||||||
- Bash: `--short-name "your-generated-short-name"`
|
- You must only ever run this script once per feature
|
||||||
- PowerShell: `-ShortName "your-generated-short-name"`
|
|
||||||
- For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot")
|
|
||||||
- You must only ever run this script once
|
|
||||||
- The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for
|
- 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.
|
3. Load `templates/spec-template.md` to understand required sections.
|
||||||
|
|
||||||
@@ -119,7 +129,7 @@ Given that feature description, do this:
|
|||||||
|
|
||||||
c. **Handle Validation Results**:
|
c. **Handle Validation Results**:
|
||||||
|
|
||||||
- **If all items pass**: Mark checklist complete and proceed to step 6
|
- **If all items pass**: Mark checklist complete and proceed to step 7
|
||||||
|
|
||||||
- **If items fail (excluding [NEEDS CLARIFICATION])**:
|
- **If items fail (excluding [NEEDS CLARIFICATION])**:
|
||||||
1. List the failing items and specific issues
|
1. List the failing items and specific issues
|
||||||
@@ -168,8 +178,6 @@ Given that feature description, do this:
|
|||||||
|
|
||||||
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
|
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
|
||||||
|
|
||||||
## General Guidelines
|
|
||||||
|
|
||||||
## Quick Guidelines
|
## Quick Guidelines
|
||||||
|
|
||||||
- Focus on **WHAT** users need and **WHY**.
|
- Focus on **WHAT** users need and **WHY**.
|
||||||
@@ -206,7 +214,7 @@ When creating this spec from a user prompt:
|
|||||||
- Performance targets: Standard web/mobile app expectations unless specified
|
- Performance targets: Standard web/mobile app expectations unless specified
|
||||||
- Error handling: User-friendly messages with appropriate fallbacks
|
- Error handling: User-friendly messages with appropriate fallbacks
|
||||||
- Authentication method: Standard session-based or OAuth2 for web apps
|
- Authentication method: Standard session-based or OAuth2 for web apps
|
||||||
- Integration patterns: RESTful APIs unless specified otherwise
|
- Integration patterns: Use project-appropriate patterns (REST/GraphQL for web services, function calls for libraries, CLI args for tools, etc.)
|
||||||
|
|
||||||
### Success Criteria Guidelines
|
### Success Criteria Guidelines
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
---
|
---
|
||||||
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
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:
|
scripts:
|
||||||
sh: scripts/bash/check-prerequisites.sh --json
|
sh: scripts/bash/check-prerequisites.sh --json
|
||||||
ps: scripts/powershell/check-prerequisites.ps1 -Json
|
ps: scripts/powershell/check-prerequisites.ps1 -Json
|
||||||
@@ -13,27 +22,61 @@ $ARGUMENTS
|
|||||||
|
|
||||||
You **MUST** consider the user input before proceeding (if not empty).
|
You **MUST** consider the user input before proceeding (if not empty).
|
||||||
|
|
||||||
|
## Pre-Execution Checks
|
||||||
|
|
||||||
|
**Check for extension hooks (before tasks generation)**:
|
||||||
|
- Check if `.specify/extensions.yml` exists in the project root.
|
||||||
|
- If it exists, read it and look for entries under the `hooks.before_tasks` key
|
||||||
|
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||||
|
- Filter to only hooks where `enabled: true`
|
||||||
|
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||||
|
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||||
|
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||||
|
- For each executable hook, output the following based on its `optional` flag:
|
||||||
|
- **Optional hook** (`optional: true`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Pre-Hook**: {extension}
|
||||||
|
Command: `/{command}`
|
||||||
|
Description: {description}
|
||||||
|
|
||||||
|
Prompt: {prompt}
|
||||||
|
To execute: `/{command}`
|
||||||
|
```
|
||||||
|
- **Mandatory hook** (`optional: false`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Automatic Pre-Hook**: {extension}
|
||||||
|
Executing: `/{command}`
|
||||||
|
EXECUTE_COMMAND: {command}
|
||||||
|
|
||||||
|
Wait for the result of the hook command before proceeding to the Outline.
|
||||||
|
```
|
||||||
|
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||||
|
|
||||||
## Outline
|
## Outline
|
||||||
|
|
||||||
1. **Setup**: 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. **Setup**: 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").
|
||||||
|
|
||||||
2. **Load design documents**: Read from FEATURE_DIR:
|
2. **Load design documents**: Read from FEATURE_DIR:
|
||||||
- **Required**: plan.md (tech stack, libraries, structure), spec.md (user stories with priorities)
|
- **Required**: plan.md (tech stack, libraries, structure), spec.md (user stories with priorities)
|
||||||
- **Optional**: data-model.md (entities), contracts/ (API endpoints), research.md (decisions), quickstart.md (test scenarios)
|
- **Optional**: data-model.md (entities), contracts/ (interface contracts), research.md (decisions), quickstart.md (test scenarios)
|
||||||
- Note: Not all projects have all documents. Generate tasks based on what's available.
|
- Note: Not all projects have all documents. Generate tasks based on what's available.
|
||||||
|
|
||||||
3. **Execute task generation workflow**:
|
3. **Execute task generation workflow**:
|
||||||
- Load plan.md and extract tech stack, libraries, project structure
|
- Load plan.md and extract tech stack, libraries, project structure
|
||||||
- Load spec.md and extract user stories with their priorities (P1, P2, P3, etc.)
|
- 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 data-model.md exists: Extract entities and map to user stories
|
||||||
- If contracts/ exists: Map endpoints to user stories
|
- If contracts/ exists: Map interface contracts to user stories
|
||||||
- If research.md exists: Extract decisions for setup tasks
|
- If research.md exists: Extract decisions for setup tasks
|
||||||
- Generate tasks organized by user story (see Task Generation Rules below)
|
- Generate tasks organized by user story (see Task Generation Rules below)
|
||||||
- Generate dependency graph showing user story completion order
|
- Generate dependency graph showing user story completion order
|
||||||
- Create parallel execution examples per user story
|
- Create parallel execution examples per user story
|
||||||
- Validate task completeness (each user story has all needed tasks, independently testable)
|
- 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
|
- Correct feature name from plan.md
|
||||||
- Phase 1: Setup tasks (project initialization)
|
- Phase 1: Setup tasks (project initialization)
|
||||||
- Phase 2: Foundational tasks (blocking prerequisites for all user stories)
|
- Phase 2: Foundational tasks (blocking prerequisites for all user stories)
|
||||||
@@ -54,6 +97,35 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
- Suggested MVP scope (typically just User Story 1)
|
- Suggested MVP scope (typically just User Story 1)
|
||||||
- Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths)
|
- Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths)
|
||||||
|
|
||||||
|
6. **Check for extension hooks**: After tasks.md is generated, check if `.specify/extensions.yml` exists in the project root.
|
||||||
|
- If it exists, read it and look for entries under the `hooks.after_tasks` key
|
||||||
|
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
|
||||||
|
- Filter to only hooks where `enabled: true`
|
||||||
|
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
|
||||||
|
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
|
||||||
|
- If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation
|
||||||
|
- For each executable hook, output the following based on its `optional` flag:
|
||||||
|
- **Optional hook** (`optional: true`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Optional Hook**: {extension}
|
||||||
|
Command: `/{command}`
|
||||||
|
Description: {description}
|
||||||
|
|
||||||
|
Prompt: {prompt}
|
||||||
|
To execute: `/{command}`
|
||||||
|
```
|
||||||
|
- **Mandatory hook** (`optional: false`):
|
||||||
|
```
|
||||||
|
## Extension Hooks
|
||||||
|
|
||||||
|
**Automatic Hook**: {extension}
|
||||||
|
Executing: `/{command}`
|
||||||
|
EXECUTE_COMMAND: {command}
|
||||||
|
```
|
||||||
|
- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently
|
||||||
|
|
||||||
Context for task generation: {ARGS}
|
Context for task generation: {ARGS}
|
||||||
|
|
||||||
The tasks.md should be immediately executable - each task must be specific enough that an LLM can complete it without additional context.
|
The tasks.md should be immediately executable - each task must be specific enough that an LLM can complete it without additional context.
|
||||||
@@ -103,13 +175,13 @@ Every task MUST strictly follow this format:
|
|||||||
- Map all related components to their story:
|
- Map all related components to their story:
|
||||||
- Models needed for that story
|
- Models needed for that story
|
||||||
- Services needed for that story
|
- Services needed for that story
|
||||||
- Endpoints/UI needed for that story
|
- Interfaces/UI needed for that story
|
||||||
- If tests requested: Tests specific to that story
|
- If tests requested: Tests specific to that story
|
||||||
- Mark story dependencies (most stories should be independent)
|
- Mark story dependencies (most stories should be independent)
|
||||||
|
|
||||||
2. **From Contracts**:
|
2. **From Contracts**:
|
||||||
- Map each contract/endpoint → to the user story it serves
|
- Map each interface contract → to the user story it serves
|
||||||
- If tests requested: Each contract → contract test task [P] before implementation in that story's phase
|
- If tests requested: Each interface contract → contract test task [P] before implementation in that story's phase
|
||||||
|
|
||||||
3. **From Data Model**:
|
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
|
||||||
|
|||||||
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
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
||||||
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
||||||
|
|
||||||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/plan-template.md` for the execution workflow.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
||||||
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
|
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
|
||||||
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
|
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
|
||||||
**Project Type**: [single/web/mobile - determines source structure]
|
**Project Type**: [e.g., library/cli/web-service/mobile-app/compiler/desktop-app or NEEDS CLARIFICATION]
|
||||||
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
|
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
|
||||||
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
|
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
|
||||||
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
||||||
|
|||||||
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"""Unit tests for Spec Kit."""
|
||||||
34
tests/hooks/.specify/extensions.yml
Normal file
34
tests/hooks/.specify/extensions.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
hooks:
|
||||||
|
before_implement:
|
||||||
|
- id: pre_test
|
||||||
|
enabled: true
|
||||||
|
optional: false
|
||||||
|
extension: "test-extension"
|
||||||
|
command: "pre_implement_test"
|
||||||
|
description: "Test before implement hook execution"
|
||||||
|
|
||||||
|
after_implement:
|
||||||
|
- id: post_test
|
||||||
|
enabled: true
|
||||||
|
optional: true
|
||||||
|
extension: "test-extension"
|
||||||
|
command: "post_implement_test"
|
||||||
|
description: "Test after implement hook execution"
|
||||||
|
prompt: "Would you like to run the post-implement test?"
|
||||||
|
|
||||||
|
before_tasks:
|
||||||
|
- id: pre_tasks_test
|
||||||
|
enabled: true
|
||||||
|
optional: false
|
||||||
|
extension: "test-extension"
|
||||||
|
command: "pre_tasks_test"
|
||||||
|
description: "Test before tasks hook execution"
|
||||||
|
|
||||||
|
after_tasks:
|
||||||
|
- id: post_tasks_test
|
||||||
|
enabled: true
|
||||||
|
optional: true
|
||||||
|
extension: "test-extension"
|
||||||
|
command: "post_tasks_test"
|
||||||
|
description: "Test after tasks hook execution"
|
||||||
|
prompt: "Would you like to run the post-tasks test?"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user