mirror of
https://github.com/github/spec-kit.git
synced 2026-08-15 14:02:04 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04fd3b8033 |
@@ -65,8 +65,7 @@
|
||||
},
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
".specify/scripts/bash/": true,
|
||||
".specify/scripts/powershell/": true,
|
||||
".specify/scripts/python/": true
|
||||
".specify/scripts/powershell/": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,17 +97,6 @@ echo -e "\n🤖 Installing CodeBuddy CLI..."
|
||||
run_command "npm install -g @tencent-ai/codebuddy-code@latest"
|
||||
echo "✅ Done"
|
||||
|
||||
echo -e "\n🤖 Installing Factory Droid CLI..."
|
||||
run_command "npm install -g droid@latest"
|
||||
|
||||
if ! command -v droid >/dev/null 2>&1; then
|
||||
echo -e "\033[0;31m[ERROR] Droid CLI installation did not create 'droid' in PATH.\033[0m" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run_command "droid --version > /dev/null"
|
||||
echo "✅ Done"
|
||||
|
||||
# Installing UV (Python package manager)
|
||||
echo -e "\n🐍 Installing UV - Python Package Manager..."
|
||||
run_command "pipx install uv"
|
||||
|
||||
@@ -8,7 +8,7 @@ body:
|
||||
value: |
|
||||
Thanks for requesting a new agent! Before submitting, please check if the agent is already supported.
|
||||
|
||||
**Currently supported agents**: Alquimia AI, Amp, Antigravity, Auggie CLI, Claude Code, Cline, CodeBuddy, Codex CLI, Command Code, Cursor, Devin for Terminal, Factory Droid, Firebender, Forge, Gemini CLI, GitHub Copilot, Goose, Grok Build, Hermes Agent, IBM Bob, Junie, Kilo Code, Kimi Code, Kiro CLI, Lingma, Mistral Vibe, Oh My Pi, opencode, Pi Coding Agent, Qoder CLI, Qwen Code, RovoDev ACLI, SHAI, Tabnine CLI, Trae, ZCode, Zed
|
||||
**Currently supported agents**: Amp, Antigravity, Auggie CLI, Claude Code, Cline, CodeBuddy, Codex CLI, Cursor, Devin for Terminal, Firebender, Forge, Gemini CLI, GitHub Copilot, Goose, Grok Build, Hermes Agent, IBM Bob, Junie, Kilo Code, Kimi Code, Kiro CLI, Lingma, Mistral Vibe, Oh My Pi, opencode, Pi Coding Agent, Qoder CLI, Qwen Code, RovoDev ACLI, SHAI, Tabnine CLI, Trae, ZCode, Zed
|
||||
|
||||
- type: input
|
||||
id: agent-name
|
||||
|
||||
@@ -62,7 +62,6 @@ body:
|
||||
label: AI Agent
|
||||
description: Which AI agent are you using?
|
||||
options:
|
||||
- Alquimia AI
|
||||
- Amp
|
||||
- Antigravity
|
||||
- Auggie CLI
|
||||
@@ -70,10 +69,8 @@ body:
|
||||
- Cline
|
||||
- CodeBuddy
|
||||
- Codex CLI
|
||||
- Command Code
|
||||
- Cursor
|
||||
- Devin for Terminal
|
||||
- Factory Droid
|
||||
- Firebender
|
||||
- Forge
|
||||
- Gemini CLI
|
||||
|
||||
@@ -16,8 +16,6 @@ body:
|
||||
- If you host a bundle catalog, test catalog installation with `specify bundle catalog add <catalog-url> --id <catalog-id> --policy install-allowed` and `specify bundle install <bundle-id>`
|
||||
- If your bundle depends on components from non-default catalogs, document those catalog URLs and test installation from a clean project
|
||||
|
||||
**After submitting:** a maintainer applies the `bundle-submission` label during issue triage, which starts the automated catalog validation. You don't need to apply any label or ask for one.
|
||||
|
||||
- type: input
|
||||
id: bundle-id
|
||||
attributes:
|
||||
|
||||
@@ -14,8 +14,6 @@ body:
|
||||
- Create a GitHub release with a version tag (e.g., v1.0.0)
|
||||
- Test installation: `specify extension add <extension-name> --from <your-release-url>`
|
||||
|
||||
**After submitting:** a maintainer applies the `extension-submission` label during issue triage, which starts the automated catalog validation. You don't need to apply any label or ask for one.
|
||||
|
||||
- type: input
|
||||
id: extension-id
|
||||
attributes:
|
||||
|
||||
@@ -56,7 +56,6 @@ body:
|
||||
description: Does this feature relate to a specific AI agent?
|
||||
options:
|
||||
- All agents
|
||||
- Alquimia AI
|
||||
- Amp
|
||||
- Antigravity
|
||||
- Auggie CLI
|
||||
@@ -64,10 +63,8 @@ body:
|
||||
- Cline
|
||||
- CodeBuddy
|
||||
- Codex CLI
|
||||
- Command Code
|
||||
- Cursor
|
||||
- Devin for Terminal
|
||||
- Factory Droid
|
||||
- Firebender
|
||||
- Forge
|
||||
- Gemini CLI
|
||||
|
||||
@@ -14,8 +14,6 @@ body:
|
||||
- Create a GitHub release with a version tag (e.g., v1.0.0)
|
||||
- Test installation from the release archive: `specify preset add --from <download-url>`
|
||||
|
||||
**After submitting:** a maintainer applies the `preset-submission` label during issue triage, which starts the automated catalog validation. You don't need to apply any label or ask for one.
|
||||
|
||||
- type: input
|
||||
id: preset-id
|
||||
attributes:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"entries": {
|
||||
"actions/checkout@v7.0.1": {
|
||||
"actions/checkout@v6.0.3": {
|
||||
"repo": "actions/checkout",
|
||||
"version": "v7.0.1",
|
||||
"sha": "3d3c42e5aac5ba805825da76410c181273ba90b1"
|
||||
"version": "v6.0.3",
|
||||
"sha": "df4cb1c069e1874edd31b4311f1884172cec0e10"
|
||||
},
|
||||
"actions/download-artifact@v8.0.1": {
|
||||
"repo": "actions/download-artifact",
|
||||
@@ -15,20 +15,10 @@
|
||||
"version": "v9.0.0",
|
||||
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
|
||||
},
|
||||
"actions/setup-node@v7.0.0": {
|
||||
"actions/setup-node@v6.4.0": {
|
||||
"repo": "actions/setup-node",
|
||||
"version": "v7.0.0",
|
||||
"sha": "820762786026740c76f36085b0efc47a31fe5020"
|
||||
},
|
||||
"actions/setup-python@v7.0.0": {
|
||||
"repo": "actions/setup-python",
|
||||
"version": "v7.0.0",
|
||||
"sha": "5fda3b95a4ea91299a34e894583c3862153e4b97"
|
||||
},
|
||||
"astral-sh/setup-uv@v9.0.0": {
|
||||
"repo": "astral-sh/setup-uv",
|
||||
"version": "v9.0.0",
|
||||
"sha": "c771a70e6277c0a99b617c7a806ffedaca235ff9"
|
||||
"version": "v6.4.0",
|
||||
"sha": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e"
|
||||
},
|
||||
"actions/upload-artifact@v7.0.1": {
|
||||
"repo": "actions/upload-artifact",
|
||||
|
||||
@@ -8,10 +8,6 @@ updates:
|
||||
- dependency-name: "github/gh-aw-actions/**"
|
||||
- dependency-name: "github/gh-aw-actions" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
|
||||
package-ecosystem: github-actions
|
||||
groups:
|
||||
codeql-action:
|
||||
patterns:
|
||||
- "github/codeql-action*"
|
||||
schedule:
|
||||
interval: weekly
|
||||
version: 2
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
"""Check that committed security audit requirements are up to date."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
COMMITTED_REQUIREMENTS = REPO_ROOT / ".github" / "security-audit-requirements.txt"
|
||||
DEPENDENCY_INPUTS = ("pyproject.toml", ".github/security-audit-requirements.txt")
|
||||
|
||||
|
||||
def _dependency_diff_refs() -> tuple[str, str]:
|
||||
base_ref = os.environ.get("DEPENDENCY_DIFF_BASE", "").strip()
|
||||
head_ref = os.environ.get("DEPENDENCY_DIFF_HEAD", "").strip() or "HEAD"
|
||||
if base_ref and not set(base_ref) <= {"0"}:
|
||||
return base_ref, head_ref
|
||||
# Fallback when no usable base is supplied (push with an all-zero
|
||||
# ``github.event.before``, manual dispatch, etc.). ``HEAD^`` fails on a
|
||||
# shallow checkout or a single-commit repo; that ``git diff`` error is
|
||||
# caught by the caller and deliberately treated as "inputs changed" so the
|
||||
# audit runs anyway — failing safe (audit) rather than skipping silently.
|
||||
return "HEAD^", "HEAD"
|
||||
|
||||
|
||||
def _dependency_inputs_changed() -> bool:
|
||||
base_ref, head_ref = _dependency_diff_refs()
|
||||
try:
|
||||
merge_base = subprocess.run(
|
||||
["git", "merge-base", base_ref, head_ref],
|
||||
check=True,
|
||||
cwd=REPO_ROOT,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
).stdout.strip()
|
||||
result = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"diff",
|
||||
"--name-only",
|
||||
merge_base,
|
||||
head_ref,
|
||||
"--",
|
||||
*DEPENDENCY_INPUTS,
|
||||
],
|
||||
check=True,
|
||||
cwd=REPO_ROOT,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
print(
|
||||
"Could not determine changed dependency inputs; checking requirements.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
if exc.stderr:
|
||||
print(exc.stderr.strip(), file=sys.stderr)
|
||||
return True
|
||||
|
||||
changed_inputs = [line for line in result.stdout.splitlines() if line]
|
||||
if not changed_inputs:
|
||||
print("Dependency audit inputs unchanged; sync check skipped.")
|
||||
return False
|
||||
|
||||
print(f"Dependency audit inputs changed: {', '.join(changed_inputs)}")
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not _dependency_inputs_changed():
|
||||
return 0
|
||||
|
||||
generated_requirements_env = os.environ.get("GENERATED_REQUIREMENTS", "").strip()
|
||||
if not generated_requirements_env:
|
||||
print(
|
||||
"GENERATED_REQUIREMENTS must be set to the temporary output file path.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
generated_requirements = Path(generated_requirements_env)
|
||||
generated_requirements.parent.mkdir(parents=True, exist_ok=True)
|
||||
generated_requirements.write_bytes(COMMITTED_REQUIREMENTS.read_bytes())
|
||||
|
||||
subprocess.run(
|
||||
[
|
||||
"uv",
|
||||
"pip",
|
||||
"compile",
|
||||
"pyproject.toml",
|
||||
"--extra",
|
||||
"test",
|
||||
"--universal",
|
||||
"--generate-hashes",
|
||||
"--quiet",
|
||||
"--no-header",
|
||||
"--output-file",
|
||||
str(generated_requirements),
|
||||
],
|
||||
check=True,
|
||||
cwd=REPO_ROOT,
|
||||
)
|
||||
|
||||
committed = COMMITTED_REQUIREMENTS.read_text(encoding="utf-8")
|
||||
generated = generated_requirements.read_text(encoding="utf-8")
|
||||
if committed == generated:
|
||||
return 0
|
||||
|
||||
print(
|
||||
"Regenerate .github/security-audit-requirements.txt with the documented "
|
||||
"uv pip compile command.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,253 +0,0 @@
|
||||
annotated-doc==0.0.5 \
|
||||
--hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 \
|
||||
--hash=sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb
|
||||
# via typer
|
||||
click==8.4.2 \
|
||||
--hash=sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6 \
|
||||
--hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76
|
||||
# via specify-cli (pyproject.toml)
|
||||
colorama==0.4.6 ; sys_platform == 'win32' \
|
||||
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
|
||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||
# via
|
||||
# click
|
||||
# pytest
|
||||
# typer
|
||||
coverage==7.15.2 \
|
||||
--hash=sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2 \
|
||||
--hash=sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e \
|
||||
--hash=sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db \
|
||||
--hash=sha256:09f5c6ec5901f667bd97dd140b5b9a2586b10efec66f46fb1e6d8135f8b95bdf \
|
||||
--hash=sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c \
|
||||
--hash=sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8 \
|
||||
--hash=sha256:1268ac8fb9ddcd783d3948dbabaf80a5d53bfdaa0575e873e2139a692f797443 \
|
||||
--hash=sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a \
|
||||
--hash=sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145 \
|
||||
--hash=sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9 \
|
||||
--hash=sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2 \
|
||||
--hash=sha256:1d16e3a7104ea84f03e614611b3edbf6fb6892554b3ab0fe7fbb3f2b2ef04376 \
|
||||
--hash=sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138 \
|
||||
--hash=sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578 \
|
||||
--hash=sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c \
|
||||
--hash=sha256:29c052f7c83ccfcc5c577eaae025d2e4a9bb80daf03c0ac31c996e83b000ce88 \
|
||||
--hash=sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036 \
|
||||
--hash=sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c \
|
||||
--hash=sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071 \
|
||||
--hash=sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a \
|
||||
--hash=sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d \
|
||||
--hash=sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b \
|
||||
--hash=sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a \
|
||||
--hash=sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b \
|
||||
--hash=sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050 \
|
||||
--hash=sha256:44826758cfe73fcd0e6af5deb4ba6d5417cc1d13df3acb35c93484a11160f846 \
|
||||
--hash=sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d \
|
||||
--hash=sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1 \
|
||||
--hash=sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660 \
|
||||
--hash=sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40 \
|
||||
--hash=sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026 \
|
||||
--hash=sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0 \
|
||||
--hash=sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b \
|
||||
--hash=sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0 \
|
||||
--hash=sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa \
|
||||
--hash=sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d \
|
||||
--hash=sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658 \
|
||||
--hash=sha256:728a33676d4c3f0db977990a4bd421dcaa3be3e53b5b6273036fff6666008e89 \
|
||||
--hash=sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072 \
|
||||
--hash=sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199 \
|
||||
--hash=sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446 \
|
||||
--hash=sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743 \
|
||||
--hash=sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7 \
|
||||
--hash=sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1 \
|
||||
--hash=sha256:7e8f27131dc7cd53de2c137dd207b3720919320b3c20d499dc30aa9ee6173287 \
|
||||
--hash=sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5 \
|
||||
--hash=sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be \
|
||||
--hash=sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688 \
|
||||
--hash=sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934 \
|
||||
--hash=sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7 \
|
||||
--hash=sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440 \
|
||||
--hash=sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984 \
|
||||
--hash=sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc \
|
||||
--hash=sha256:9b5bd92ff1ec22e535eab0de75fa6db021992791f461a2aceb7822c625a1187d \
|
||||
--hash=sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098 \
|
||||
--hash=sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6 \
|
||||
--hash=sha256:9f4432898c4bf2fba0435bbe35dd4437d7264565e5a88a21f5b49d8662a6b629 \
|
||||
--hash=sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b \
|
||||
--hash=sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee \
|
||||
--hash=sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f \
|
||||
--hash=sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1 \
|
||||
--hash=sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad \
|
||||
--hash=sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3 \
|
||||
--hash=sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9 \
|
||||
--hash=sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3 \
|
||||
--hash=sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a \
|
||||
--hash=sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296 \
|
||||
--hash=sha256:affd532502d34c0472d0cdb181325c89f1d2c44992fef0c17e88e7b1576259a1 \
|
||||
--hash=sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd \
|
||||
--hash=sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73 \
|
||||
--hash=sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f \
|
||||
--hash=sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0 \
|
||||
--hash=sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6 \
|
||||
--hash=sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5 \
|
||||
--hash=sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1 \
|
||||
--hash=sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589 \
|
||||
--hash=sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688 \
|
||||
--hash=sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487 \
|
||||
--hash=sha256:d17d7512151fedfcc64c1821a8977fc9be0dbf495754669afcab7b57abc98ae9 \
|
||||
--hash=sha256:d46e62cb35d91e6e2589fda6d28074426b0e276422b5d2ebef2c6b11dc60dbfd \
|
||||
--hash=sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee \
|
||||
--hash=sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d \
|
||||
--hash=sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d \
|
||||
--hash=sha256:dfd3db045e95960ae3683059571e597fda7cc610106a8916f77c5839048c1deb \
|
||||
--hash=sha256:e26ff680768b8095e8874aabe0e9d3a47a2a9f176a8340d05f8604c56457c23a \
|
||||
--hash=sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328 \
|
||||
--hash=sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635 \
|
||||
--hash=sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188 \
|
||||
--hash=sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c \
|
||||
--hash=sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243 \
|
||||
--hash=sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a
|
||||
# via pytest-cov
|
||||
iniconfig==2.3.0 \
|
||||
--hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \
|
||||
--hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12
|
||||
# via pytest
|
||||
json5==0.15.0 \
|
||||
--hash=sha256:56636a30c0e8a4665fe2179c0212f32eae3796dea89ea6f649b9436ecdb39618 \
|
||||
--hash=sha256:7424d1f1eb1d56da6e3d70643f53619862b4ce81440bdb8ecfd6f875e5ba4a71
|
||||
# via specify-cli (pyproject.toml)
|
||||
markdown-it-py==4.2.0 \
|
||||
--hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \
|
||||
--hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a
|
||||
# via rich
|
||||
mdurl==0.1.2 \
|
||||
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
|
||||
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
|
||||
# via markdown-it-py
|
||||
packaging==26.2 \
|
||||
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
||||
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
||||
# via
|
||||
# specify-cli (pyproject.toml)
|
||||
# pytest
|
||||
pathspec==1.1.1 \
|
||||
--hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \
|
||||
--hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189
|
||||
# via specify-cli (pyproject.toml)
|
||||
platformdirs==4.11.0 \
|
||||
--hash=sha256:0555d18370482847566ffabcaa53ad7c6c1c29f195989ae1ed634a05f76ea1e0 \
|
||||
--hash=sha256:360ccded2b7fce0af0ff80cc8f5942a1c5d99b0e856033acb030bfc634709e74
|
||||
# via specify-cli (pyproject.toml)
|
||||
pluggy==1.6.0 \
|
||||
--hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \
|
||||
--hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
|
||||
# via
|
||||
# pytest
|
||||
# pytest-cov
|
||||
pygments==2.20.0 \
|
||||
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
|
||||
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
|
||||
# via
|
||||
# pytest
|
||||
# rich
|
||||
pytest==9.1.1 \
|
||||
--hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \
|
||||
--hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c
|
||||
# via
|
||||
# specify-cli (pyproject.toml)
|
||||
# pytest-cov
|
||||
pytest-cov==7.1.0 \
|
||||
--hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \
|
||||
--hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678
|
||||
# via specify-cli (pyproject.toml)
|
||||
pyyaml==6.0.3 \
|
||||
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
|
||||
--hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \
|
||||
--hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
|
||||
--hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
|
||||
--hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
|
||||
--hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
|
||||
--hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
|
||||
--hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
|
||||
--hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \
|
||||
--hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
|
||||
--hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
|
||||
--hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \
|
||||
--hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \
|
||||
--hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
|
||||
--hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \
|
||||
--hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
|
||||
--hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
|
||||
--hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \
|
||||
--hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \
|
||||
--hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
|
||||
--hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
|
||||
--hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
|
||||
--hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
|
||||
--hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
|
||||
--hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
|
||||
--hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
|
||||
--hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
|
||||
--hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \
|
||||
--hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
|
||||
--hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \
|
||||
--hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
|
||||
--hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
|
||||
--hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \
|
||||
--hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
|
||||
--hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
|
||||
--hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
|
||||
--hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
|
||||
--hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
|
||||
--hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
|
||||
--hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
|
||||
--hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
|
||||
--hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
|
||||
--hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
|
||||
--hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
|
||||
--hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
|
||||
--hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
|
||||
--hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
|
||||
--hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
|
||||
--hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
|
||||
--hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \
|
||||
--hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
|
||||
--hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
|
||||
--hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
|
||||
--hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
|
||||
--hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
|
||||
--hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
|
||||
--hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \
|
||||
--hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
|
||||
--hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
|
||||
--hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
|
||||
--hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
|
||||
--hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \
|
||||
--hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \
|
||||
--hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
|
||||
--hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
|
||||
--hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
|
||||
--hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
|
||||
--hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
|
||||
--hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
|
||||
--hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \
|
||||
--hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
|
||||
--hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \
|
||||
--hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
|
||||
# via specify-cli (pyproject.toml)
|
||||
readchar==4.2.2 \
|
||||
--hash=sha256:92daf7e42c52b0787e6c75d01ecfb9a94f4ceff3764958b570c1dddedd47b200 \
|
||||
--hash=sha256:e3b270fe16fc90c50ac79107700330a133dd4c63d22939f5b03b4f24564d5dd8
|
||||
# via specify-cli (pyproject.toml)
|
||||
rich==15.0.0 \
|
||||
--hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \
|
||||
--hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36
|
||||
# via
|
||||
# specify-cli (pyproject.toml)
|
||||
# typer
|
||||
shellingham==1.5.4 \
|
||||
--hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \
|
||||
--hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de
|
||||
# via typer
|
||||
typer==0.27.0 \
|
||||
--hash=sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5 \
|
||||
--hash=sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1
|
||||
# via specify-cli (pyproject.toml)
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"c64e3dc29aca89e48108bb6d4eb877f6264b4cec9cd56dcd36827893802d2a64","body_hash":"cade22e5083254b735200f4ff7d686104e4ccab848ff7355141b9689354834db","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
@@ -33,10 +33,10 @@
|
||||
# - GITHUB_TOKEN
|
||||
#
|
||||
# Custom actions used:
|
||||
# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
# - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
# - github/gh-aw-actions/setup@c0338fef4749d08c21f8f975fb0e37efa17dda47 # v0.79.8
|
||||
#
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
- name: Checkout .github and .agents folders
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -1352,7 +1352,7 @@ jobs:
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository for patch context
|
||||
if: needs.agent.outputs.has_patch == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
# --- Threat Detection ---
|
||||
@@ -1419,7 +1419,7 @@ jobs:
|
||||
mkdir -p /tmp/gh-aw/threat-detection
|
||||
touch /tmp/gh-aw/threat-detection/detection.log
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '24'
|
||||
package-manager-cache: false
|
||||
@@ -1678,7 +1678,7 @@ jobs:
|
||||
await main();
|
||||
- name: Checkout repository (trusted default branch for comment events)
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1686,7 +1686,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Checkout repository
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
+12
-12
@@ -1,5 +1,5 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f9532e77722bfd32e8f626cbfbf6c5372ddcd9997963d965d53e8531b3e28a15","body_hash":"83b7e917f475d6ddf32f17e7da09dd4097a01dddbcbbf8eeec673912285de8b2","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"687ea37b376b3b918331c13fce6cdbf5b9898bab8e514ca57b662b92b6d3cd2c","body_hash":"83b7e917f475d6ddf32f17e7da09dd4097a01dddbcbbf8eeec673912285de8b2","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
@@ -33,7 +33,7 @@
|
||||
# - GITHUB_TOKEN
|
||||
#
|
||||
# Custom actions used:
|
||||
# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
# - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
- name: Checkout .github and .agents folders
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -511,9 +511,9 @@ jobs:
|
||||
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
|
||||
mkdir -p /tmp/gh-aw/safeoutputs
|
||||
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_253299f841a5ea45_EOF'
|
||||
{"add_comment":{"max":2},"add_labels":{"allowed":["extension-submission","validation-passed","validation-failed","needs-info"],"max":3},"create_pull_request":{"allowed_files":["extensions/catalog.community.json","docs/community/extensions.md"],"draft":true,"labels":["extension-submission","automated"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","CONTRIBUTING.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"blocked","title_prefix":"[extension] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_253299f841a5ea45_EOF
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7b8091d12bfe1e7b_EOF'
|
||||
{"add_comment":{"max":2},"add_labels":{"allowed":["extension-submission","validation-passed","validation-failed","needs-info"],"max":3},"create_pull_request":{"draft":true,"labels":["extension-submission","automated"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","CONTRIBUTING.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"blocked","title_prefix":"[extension] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_7b8091d12bfe1e7b_EOF
|
||||
- name: Generate Safe Outputs Tools
|
||||
env:
|
||||
GH_AW_TOOLS_META_JSON: |
|
||||
@@ -1332,7 +1332,7 @@ jobs:
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository for patch context
|
||||
if: needs.agent.outputs.has_patch == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# --- Threat Detection ---
|
||||
@@ -1658,7 +1658,7 @@ jobs:
|
||||
await main();
|
||||
- name: Checkout repository (trusted default branch for comment events)
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1666,7 +1666,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Checkout repository
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1705,7 +1705,7 @@ jobs:
|
||||
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
|
||||
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||
GITHUB_API_URL: ${{ github.api_url }}
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":2},\"add_labels\":{\"allowed\":[\"extension-submission\",\"validation-passed\",\"validation-failed\",\"needs-info\"],\"max\":3},\"create_pull_request\":{\"allowed_files\":[\"extensions/catalog.community.json\",\"docs/community/extensions.md\"],\"draft\":true,\"labels\":[\"extension-submission\",\"automated\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"blocked\",\"title_prefix\":\"[extension] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":2},\"add_labels\":{\"allowed\":[\"extension-submission\",\"validation-passed\",\"validation-failed\",\"needs-info\"],\"max\":3},\"create_pull_request\":{\"draft\":true,\"labels\":[\"extension-submission\",\"automated\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"blocked\",\"title_prefix\":\"[extension] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
|
||||
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -31,9 +31,6 @@ safe-outputs:
|
||||
labels: [extension-submission, automated]
|
||||
draft: true
|
||||
max: 1
|
||||
allowed-files:
|
||||
- extensions/catalog.community.json
|
||||
- docs/community/extensions.md
|
||||
protected-files:
|
||||
policy: blocked
|
||||
exclude:
|
||||
|
||||
+12
-12
@@ -1,5 +1,5 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"09fb89e95c57c7beeaa0c823fb35f38d5f9db898a419e59dd0a323e7a9209753","body_hash":"cb6c19088fa13da0a8320c174e8c14c4887d2c8a005a5cb2d2d2faa3f890de39","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"cb6c19088fa13da0a8320c174e8c14c4887d2c8a005a5cb2d2d2faa3f890de39","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
@@ -33,7 +33,7 @@
|
||||
# - GITHUB_TOKEN
|
||||
#
|
||||
# Custom actions used:
|
||||
# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
# - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
- name: Checkout .github and .agents folders
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -511,9 +511,9 @@ jobs:
|
||||
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
|
||||
mkdir -p /tmp/gh-aw/safeoutputs
|
||||
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_429ff69f52872d0b_EOF'
|
||||
{"add_comment":{"max":2},"add_labels":{"allowed":["preset-submission","validation-passed","validation-failed","needs-info"],"max":3},"create_pull_request":{"allowed_files":["presets/catalog.community.json","docs/community/presets.md"],"draft":true,"labels":["preset-submission","automated"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","CONTRIBUTING.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"blocked","title_prefix":"[preset] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_429ff69f52872d0b_EOF
|
||||
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_78499ff7c917c441_EOF'
|
||||
{"add_comment":{"max":2},"add_labels":{"allowed":["preset-submission","validation-passed","validation-failed","needs-info"],"max":3},"create_pull_request":{"draft":true,"labels":["preset-submission","automated"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","CONTRIBUTING.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"protected_files_policy":"blocked","title_prefix":"[preset] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
|
||||
GH_AW_SAFE_OUTPUTS_CONFIG_78499ff7c917c441_EOF
|
||||
- name: Generate Safe Outputs Tools
|
||||
env:
|
||||
GH_AW_TOOLS_META_JSON: |
|
||||
@@ -1332,7 +1332,7 @@ jobs:
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository for patch context
|
||||
if: needs.agent.outputs.has_patch == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# --- Threat Detection ---
|
||||
@@ -1658,7 +1658,7 @@ jobs:
|
||||
await main();
|
||||
- name: Checkout repository (trusted default branch for comment events)
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1666,7 +1666,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Checkout repository
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1705,7 +1705,7 @@ jobs:
|
||||
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
|
||||
GITHUB_SERVER_URL: ${{ github.server_url }}
|
||||
GITHUB_API_URL: ${{ github.api_url }}
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":2},\"add_labels\":{\"allowed\":[\"preset-submission\",\"validation-passed\",\"validation-failed\",\"needs-info\"],\"max\":3},\"create_pull_request\":{\"allowed_files\":[\"presets/catalog.community.json\",\"docs/community/presets.md\"],\"draft\":true,\"labels\":[\"preset-submission\",\"automated\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"blocked\",\"title_prefix\":\"[preset] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
|
||||
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":2},\"add_labels\":{\"allowed\":[\"preset-submission\",\"validation-passed\",\"validation-failed\",\"needs-info\"],\"max\":3},\"create_pull_request\":{\"draft\":true,\"labels\":[\"preset-submission\",\"automated\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"protected_files_policy\":\"blocked\",\"title_prefix\":\"[preset] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
|
||||
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -31,9 +31,6 @@ safe-outputs:
|
||||
labels: [preset-submission, automated]
|
||||
draft: true
|
||||
max: 1
|
||||
allowed-files:
|
||||
- presets/catalog.community.json
|
||||
- docs/community/presets.md
|
||||
protected-files:
|
||||
policy: blocked
|
||||
exclude:
|
||||
|
||||
Generated
+5
-5
@@ -1,5 +1,5 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"00c226f69fb7ec2b63755304328cee6ecddbcedbe4a9840310e5f430bd3949f0","body_hash":"44428ecd81ba0e5ed7bb16436052e6cc3479fe4ad02414812e574d17830a464e","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
@@ -32,7 +32,7 @@
|
||||
# - GITHUB_TOKEN
|
||||
#
|
||||
# Custom actions used:
|
||||
# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
# - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
- name: Checkout .github and .agents folders
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
@@ -430,7 +430,7 @@ jobs:
|
||||
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -1277,7 +1277,7 @@ jobs:
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository for patch context
|
||||
if: needs.agent.outputs.has_patch == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# --- Threat Detection ---
|
||||
|
||||
Generated
+7
-7
@@ -1,5 +1,5 @@
|
||||
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"aafdb01f262d603577971994522575829802b93d9042d62446313955485df558","body_hash":"4596de2b7de95c7c73c05caedc5c1e97724b39d09d21e9b0dbfc8b570312798a","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
|
||||
# This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
|
||||
#
|
||||
# ___ _ _
|
||||
@@ -33,7 +33,7 @@
|
||||
# - GITHUB_TOKEN
|
||||
#
|
||||
# Custom actions used:
|
||||
# - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
# - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
- name: Checkout .github and .agents folders
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: |
|
||||
@@ -434,7 +434,7 @@ jobs:
|
||||
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@@ -1338,7 +1338,7 @@ jobs:
|
||||
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout repository for patch context
|
||||
if: needs.agent.outputs.has_patch == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
# --- Threat Detection ---
|
||||
@@ -1664,7 +1664,7 @@ jobs:
|
||||
await main();
|
||||
- name: Checkout repository (trusted default branch for comment events)
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
@@ -1672,7 +1672,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Checkout repository
|
||||
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
|
||||
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
Generated
+11
-22
File diff suppressed because one or more lines are too long
@@ -60,22 +60,6 @@ permissions:
|
||||
checkout:
|
||||
fetch-depth: 0
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- pypi.org
|
||||
- files.pythonhosted.org
|
||||
|
||||
steps:
|
||||
- name: Setup uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.14"
|
||||
- name: Install Python test dependencies
|
||||
run: uv pip install --system -e ".[test]"
|
||||
|
||||
safe-outputs:
|
||||
noop:
|
||||
report-as-issue: false
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
name: "Catalog: Notify submission"
|
||||
name: "Catalog: Auto-assign submission"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, labeled]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
assign:
|
||||
if: >
|
||||
(github.event.action == 'opened' && (
|
||||
contains(github.event.issue.labels.*.name, 'extension-submission') ||
|
||||
@@ -24,8 +24,24 @@ jobs:
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const assigned = (issue.assignees || []).map(a => a.login);
|
||||
const marker = '<!-- catalog-assign-bot -->';
|
||||
|
||||
// Assign mnriem if not already assigned
|
||||
if (!assigned.includes('mnriem')) {
|
||||
try {
|
||||
await github.rest.issues.addAssignees({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
assignees: ['mnriem'],
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(`Warning: could not assign mnriem: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Post team notification if not already posted
|
||||
const comments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
|
||||
@@ -19,14 +19,14 @@ jobs:
|
||||
language: [ 'actions', 'python' ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4
|
||||
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4
|
||||
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for git info
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Run markdownlint-cli2
|
||||
uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
|
||||
uses: DavidAnson/markdownlint-cli2-action@8de2aa07cae85fd17c0b35642db70cf5495f1d25 # v24.0.0
|
||||
with:
|
||||
globs: |
|
||||
'**/*.md'
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
# shellcheck is preinstalled on ubuntu-latest runners.
|
||||
# Start at --severity=error to block real bugs without flagging style
|
||||
|
||||
@@ -27,15 +27,15 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout release tag
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
path: dist/
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
||||
|
||||
- name: Publish to PyPI
|
||||
run: uv publish
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.RELEASE_PAT }}
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
name: Security Audit
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
schedule:
|
||||
- cron: "17 4 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dependency-audit:
|
||||
name: Dependency audit
|
||||
if: ${{ github.event_name != 'schedule' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.14"
|
||||
|
||||
- name: Check committed audit requirements are current
|
||||
env:
|
||||
DEPENDENCY_DIFF_BASE: ${{ github.event.pull_request.base.sha || github.event.before || '' }}
|
||||
DEPENDENCY_DIFF_HEAD: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
GENERATED_REQUIREMENTS: ${{ runner.temp }}/security-audit-requirements.txt
|
||||
run: python .github/scripts/check_security_requirements.py
|
||||
|
||||
- name: Run pip-audit (committed requirements)
|
||||
run: uvx --from pip-audit==2.10.0 pip-audit --disable-pip --require-hashes -r .github/security-audit-requirements.txt --progress-spinner off
|
||||
|
||||
dependency-audit-scheduled:
|
||||
name: Dependency audit scheduled (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
# The committed .github/security-audit-requirements.txt is generated with
|
||||
# --universal (resolves across all interpreters/platforms) and is what
|
||||
# push/PR/workflow_dispatch runs audit. The scheduled job instead compiles
|
||||
# per matrix entry with --python-version so it can surface advisories in
|
||||
# wheels that only resolve on a specific interpreter (e.g. 3.11-only) —
|
||||
# coverage the universal file may not exercise. This broadening is
|
||||
# intentional; non-scheduled runs trade that depth for determinism against
|
||||
# the committed snapshot.
|
||||
- name: Compile scheduled audit requirements
|
||||
run: |
|
||||
uv pip compile pyproject.toml --extra test --python-version "${{ matrix.python-version }}" --upgrade --generate-hashes --quiet --output-file "${{ runner.temp }}/spec-kit-audit-requirements.txt"
|
||||
|
||||
- name: Run pip-audit (scheduled live resolution)
|
||||
run: uvx --from pip-audit==2.10.0 pip-audit --disable-pip --require-hashes -r "${{ runner.temp }}/spec-kit-audit-requirements.txt" --progress-spinner off
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10
|
||||
with:
|
||||
# Days of inactivity before an issue or PR becomes stale
|
||||
days-before-stale: 150
|
||||
|
||||
@@ -13,18 +13,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
||||
with:
|
||||
python-version: "3.14"
|
||||
|
||||
- name: Run ruff check
|
||||
run: uvx ruff@0.15.0 check src tests
|
||||
run: uvx ruff check src tests
|
||||
|
||||
pytest:
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -34,13 +34,13 @@ jobs:
|
||||
python-version: ["3.13", "3.14"]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
||||
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
|
||||
@@ -10,20 +10,6 @@ The toolkit supports multiple AI coding assistants, allowing teams to use their
|
||||
|
||||
---
|
||||
|
||||
## Quickstart — Add a New Integration in 5 Steps
|
||||
|
||||
If you are new to the codebase and want to add support for a new AI agent, here is the shortest path from zero to a working integration:
|
||||
|
||||
1. **Choose a base class** — most agents only need `MarkdownIntegration`. See [Choose a base class](#1-choose-a-base-class).
|
||||
2. **Create a subpackage** — add `src/specify_cli/integrations/<package_dir>/__init__.py` with the required `key`, `config`, and `registrar_config` fields.
|
||||
3. **Register it** — add one import and one `_register()` call in `src/specify_cli/integrations/__init__.py` (both alphabetical).
|
||||
4. **Write a test file** — create `tests/integrations/test_integration_<key>.py` (hyphens in the key become underscores in the filename).
|
||||
5. **Run and verify** — use `specify init --integration <key>` to exercise the full install/uninstall cycle.
|
||||
|
||||
Each step is expanded under [Adding a New Integration](#adding-a-new-integration). Note that agent **context files** (`CLAUDE.md`, `AGENTS.md`, …) are **not** handled by the integration — that is owned by the opt-in `agent-context` extension; see [Context file behavior](#4-context-file-behavior).
|
||||
|
||||
---
|
||||
|
||||
## Integration Architecture
|
||||
|
||||
Each AI agent is a self-contained **integration subpackage** under `src/specify_cli/integrations/<key>/`. The subpackage exposes a single class that declares all metadata and inherits setup/teardown logic from a base class. Built-in integrations are then instantiated and added to the global `INTEGRATION_REGISTRY` by `src/specify_cli/integrations/__init__.py` via `_register_builtins()`.
|
||||
@@ -48,30 +34,6 @@ The registry is the **single source of truth for Python integration metadata**.
|
||||
|
||||
---
|
||||
|
||||
## IntegrationManifest — File Tracking
|
||||
|
||||
`manifest.py` provides the `IntegrationManifest` class, which records every file an integration installs. This record is what makes uninstall reliable and safe.
|
||||
|
||||
### How it works
|
||||
|
||||
`setup()` receives an `IntegrationManifest` and writes files through it rather than touching the filesystem directly:
|
||||
|
||||
```python
|
||||
# Produce a new file and record its hash for later verification.
|
||||
manifest.record_file("commands/speckit.plan.md", processed_content)
|
||||
|
||||
# Adopt a pre-existing file the integration is now responsible for.
|
||||
manifest.record_existing(".vscode/settings.json")
|
||||
```
|
||||
|
||||
The manifest is persisted at `.specify/integrations/<key>.manifest.json` (one per integration, keyed by `key`) and stores a SHA-256 hash per file. When the user runs `specify integration uninstall <key>`, `teardown()` delegates to `manifest.uninstall()`, which removes only files whose current hash still matches the recorded value — so files the user later edited by hand are skipped, not clobbered (use `specify integration uninstall <key> --force` to remove modified tracked files anyway).
|
||||
|
||||
### Why this matters
|
||||
|
||||
Without hash-tracked manifests, uninstall would either remove files it should not (destructive) or leave orphans behind (messy). If you write a custom `setup()`, route **every** file you create through `manifest.record_file(...)` (or `record_existing(...)` for files you adopt) so uninstall can reason about them.
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Integration
|
||||
|
||||
### 1. Choose a base class
|
||||
@@ -102,14 +64,13 @@ class KilocodeIntegration(MarkdownIntegration):
|
||||
key = "kilocode"
|
||||
config = {
|
||||
"name": "Kilo Code",
|
||||
"folder": ".kilo/",
|
||||
"commands_subdir": "commands",
|
||||
"folder": ".kilocode/",
|
||||
"commands_subdir": "workflows",
|
||||
"install_url": None,
|
||||
"requires_cli": False,
|
||||
}
|
||||
registrar_config = {
|
||||
"dir": ".kilo/commands",
|
||||
"legacy_dir": ".kilocode/workflows",
|
||||
"dir": ".kilocode/workflows",
|
||||
"format": "markdown",
|
||||
"args": "$ARGUMENTS",
|
||||
"extension": ".md",
|
||||
@@ -226,7 +187,7 @@ context_markers:
|
||||
end: "<!-- SPECKIT END -->"
|
||||
```
|
||||
|
||||
- The Specify CLI does **not** write this config. When `context_file` is empty, the extension's bundled scripts self-seed it by looking up the active integration's key in the extension's own `agent-context-defaults.json` map (`extensions/agent-context/scripts/bash/update-agent-context.sh`, `.ps1`, and `extensions/agent-context/scripts/python/update_agent_context.py`). The CLI registry is never consulted — all agent→context-file knowledge lives inside the extension.
|
||||
- The Specify CLI does **not** write this config. When `context_file` is empty, the extension's bundled scripts self-seed it by looking up the active integration's key in the extension's own `agent-context-defaults.json` map (`extensions/agent-context/scripts/bash/update-agent-context.sh` and `.ps1`). The CLI registry is never consulted — all agent→context-file knowledge lives inside the extension.
|
||||
- `context_markers.{start,end}` are read solely by the extension's scripts; they default to the Spec Kit markers shown above and can be customized by editing `agent-context-config.yml` directly.
|
||||
|
||||
Existing projects created by older Spec Kit versions keep working: any previously written managed section or extension config is left intact and is only ever updated by the extension when run.
|
||||
@@ -240,8 +201,8 @@ Only add custom setup logic when the agent needs non-standard behavior. Integrat
|
||||
specify init my-project --integration <key>
|
||||
|
||||
# Verify files were created in the commands directory configured by
|
||||
# config["folder"] + config["commands_subdir"] (for example, .kilo/commands/)
|
||||
ls -R my-project/.kilo/commands/
|
||||
# config["folder"] + config["commands_subdir"] (for example, .kilocode/workflows/)
|
||||
ls -R my-project/.kilocode/workflows/
|
||||
|
||||
# Uninstall cleanly
|
||||
cd my-project && specify integration uninstall <key>
|
||||
@@ -260,13 +221,13 @@ The base classes handle most work automatically. Override only when the agent de
|
||||
| Override | When to use | Example |
|
||||
|---|---|---|
|
||||
| `command_filename(template_name)` | Custom file naming or extension | Copilot → `speckit.{name}.agent.md` |
|
||||
| `options()` | Integration-specific CLI flags via `--integration-options` | Codex → `--skills` flag, Copilot → `--commands` flag |
|
||||
| `setup()` | Custom install logic (companion files, settings merge) | Copilot → `speckit-<name>/SKILL.md` (default) or `.agent.md` + `.prompt.md` + `.vscode/settings.json` (`--commands`) |
|
||||
| `options()` | Integration-specific CLI flags via `--integration-options` | Codex → `--skills` flag, Copilot → `--skills` flag |
|
||||
| `setup()` | Custom install logic (companion files, settings merge) | Copilot → `.agent.md` + `.prompt.md` + `.vscode/settings.json` (default) or `speckit-<name>/SKILL.md` (skills mode) |
|
||||
| `teardown()` | Custom uninstall logic | Rarely needed; base handles manifest-tracked files |
|
||||
|
||||
**Example — Copilot (fully custom `setup`):**
|
||||
|
||||
Copilot extends `IntegrationBase` directly because it supports two layouts. It scaffolds `speckit-<name>/SKILL.md` under `.github/skills/` by default using composition with an internal `_CopilotSkillsHelper`. Its `--commands` mode creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
|
||||
Copilot extends `IntegrationBase` directly because it creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. It also supports a `--skills` mode that scaffolds `speckit-<name>/SKILL.md` under `.github/skills/` using composition with an internal `_CopilotSkillsHelper`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
|
||||
|
||||
### 7. Update Devcontainer files (Optional)
|
||||
|
||||
@@ -307,25 +268,6 @@ echo "✅ Done"
|
||||
|
||||
## Command File Formats
|
||||
|
||||
### Script References (`scripts:` frontmatter)
|
||||
|
||||
Core command templates (`templates/commands/*.md`) that invoke a helper script declare it in a `scripts:` frontmatter block with one line per supported script type. The `{SCRIPT}` placeholder in the command body is replaced at install time with the entry matching the project's selected script type (`--script sh|ps|py`):
|
||||
|
||||
```yaml
|
||||
scripts:
|
||||
sh: scripts/bash/setup-plan.sh --json
|
||||
ps: scripts/powershell/setup-plan.ps1 -Json
|
||||
py: scripts/python/setup_plan.py --json
|
||||
```
|
||||
|
||||
| Key | Script type | Location |
|
||||
| ---- | ---------------------- | -------------------------- |
|
||||
| `sh` | POSIX shell (bash/zsh) | `scripts/bash/*.sh` |
|
||||
| `ps` | PowerShell | `scripts/powershell/*.ps1` |
|
||||
| `py` | Python | `scripts/python/*.py` |
|
||||
|
||||
All three entries must be present and behaviorally equivalent — agents parse the same stdout contract (`FEATURE_DIR:…`, `AVAILABLE_DOCS:…`, `--json` shapes) regardless of which one runs. (The bundled `agent-context` and `git` extension command templates also invoke helpers but do not yet use `scripts:` frontmatter — see [Script Types and Migration](#script-types-and-migration).)
|
||||
|
||||
### Markdown Format
|
||||
|
||||
**Standard format:**
|
||||
@@ -386,57 +328,45 @@ Different agents use different argument placeholders. The placeholder used in co
|
||||
- **TOML-based**: `{{args}}` (e.g., Gemini)
|
||||
- **YAML-based**: `{{args}}` (e.g., Goose)
|
||||
- **Custom**: some agents override the default (e.g., Forge uses `{{parameters}}`)
|
||||
- **Script placeholders**: `{SCRIPT}` (replaced with the resolved command from the template's `scripts:` frontmatter, per the project's `--script sh|ps|py` selection)
|
||||
- **Script placeholders**: `{SCRIPT}` (replaced with actual script path)
|
||||
- **Agent placeholders**: `__AGENT__` (replaced with agent name)
|
||||
|
||||
## Script Types and Migration
|
||||
|
||||
Spec Kit ships every core workflow script in three interchangeable variants — POSIX shell (`sh`), PowerShell (`ps`), and Python (`py`) — selected per project with `specify init --script sh|ps|py`. Each core command template that invokes a helper script carries all three in its `scripts:` frontmatter (templates that don't call a script, e.g. `constitution`/`specify`, have no `scripts:` block); see [Script References](#script-references-scripts-frontmatter).
|
||||
|
||||
### Why Python is recommended
|
||||
|
||||
- **No extra runtime.** The `specify` CLI is already Python, so the interpreter is guaranteed present — `py` adds no new dependency.
|
||||
- **Path toward a single source of truth.** The shell variants require paired `.sh` + `.ps1` maintenance and diverge on JSON handling (`jq` vs manual parsing). The Python variant avoids `jq` and is intended to eventually replace that dual-maintenance — but that consolidation has not happened yet: all three variants are still maintained in parallel (see the parity rule below).
|
||||
- **Parity-tested.** The Python ports are covered by tests — output-parity tests against the shell scripts where the contract is stdout-based, and direct unit tests elsewhere — so the stdout contract agents rely on stays stable.
|
||||
|
||||
### Defaults and availability
|
||||
|
||||
- `py` is available today for the core command templates (via their `scripts:` frontmatter). The bundled extensions (`agent-context`, `git`) ship Python script variants on disk, but their command templates still hard-code the Bash/PowerShell invocations, so `--script py` does not yet route those extension commands to Python — wiring `py` into the extension command templates is tracked separately.
|
||||
- Selection is per project: interactive `specify init` prompts for the script type, while non-interactive runs default to a shell variant by OS (`sh` on Linux/macOS, `ps` on Windows). `py` is chosen at the prompt or via `--script py`.
|
||||
- `sh` and `ps` remain fully supported. Nothing is removed, and `py` is not yet the default.
|
||||
|
||||
### Parity rule for contributors
|
||||
|
||||
All three script types are first-class: any change to a workflow script must update `sh`, `ps`, and `py` together and keep their tests (parity and unit) green. Making `py` the default and eventually retiring `sh`/`ps` is future work gated on adoption, tracked under the script-unification epic ([#3277](https://github.com/github/spec-kit/issues/3277)) — not something to act on from this doc.
|
||||
|
||||
## Special Processing Requirements
|
||||
|
||||
Some agents require custom processing beyond the standard template transformations:
|
||||
|
||||
### Copilot Integration
|
||||
|
||||
GitHub Copilot uses skills by default, scaffolded as
|
||||
`speckit-<name>/SKILL.md` under `.github/skills/`.
|
||||
GitHub Copilot has unique requirements:
|
||||
|
||||
**Commands mode (`--commands`):** Copilot also supports a commands-based layout
|
||||
via `--integration-options="--commands"`. When enabled:
|
||||
|
||||
- Commands use `.agent.md` extension under `.github/agents/`
|
||||
- Commands use `.agent.md` extension (not `.md`)
|
||||
- Each command gets a companion `.prompt.md` file in `.github/prompts/`
|
||||
- `.vscode/settings.json` is merged with prompt file recommendations
|
||||
- `build_command_invocation()` returns bare args for `--agent` dispatch
|
||||
- Installs `.vscode/settings.json` with prompt file recommendations
|
||||
- Context file lives at `.github/copilot-instructions.md`
|
||||
|
||||
In the default skills mode, no companion prompts or VS Code settings merge are
|
||||
created, and `build_command_invocation()` returns `/speckit-<stem>`.
|
||||
Implementation: Extends `IntegrationBase` with custom `setup()` method that:
|
||||
|
||||
1. Processes templates with `process_template()`
|
||||
2. Generates companion `.prompt.md` files
|
||||
3. Merges VS Code settings
|
||||
|
||||
**Skills mode (`--skills`):** Copilot also supports an alternative skills-based layout
|
||||
via `--integration-options="--skills"`. When enabled:
|
||||
|
||||
- Commands are scaffolded as `speckit-<name>/SKILL.md` under `.github/skills/`
|
||||
- No companion `.prompt.md` files are generated
|
||||
- No `.vscode/settings.json` merge
|
||||
- `post_process_skill_content()` injects a `mode: speckit.<stem>` frontmatter field
|
||||
- `build_command_invocation()` returns `/speckit-<stem>` instead of bare args
|
||||
|
||||
The two modes are mutually exclusive — a project uses one or the other:
|
||||
|
||||
```bash
|
||||
# Default skills mode: speckit-<name>/SKILL.md under .github/skills/
|
||||
# Default mode: .agent.md agents + .prompt.md companions + settings merge
|
||||
specify init my-project --integration copilot
|
||||
|
||||
# Commands mode: .agent.md agents + .prompt.md companions + settings merge
|
||||
specify init my-project --integration copilot --integration-options="--commands"
|
||||
# Skills mode: speckit-<name>/SKILL.md under .github/skills/
|
||||
specify init my-project --integration copilot --integration-options="--skills"
|
||||
```
|
||||
|
||||
### Forge Integration
|
||||
@@ -501,11 +431,6 @@ When an issue exists, include its number immediately after the prefix — this i
|
||||
|
||||
Disclosure is **continuous**, not a one-time event. A single AI-disclosure paragraph in the PR body does **not** cover the commits and replies you add during review rounds. Each of the following must independently attest to agent authorship.
|
||||
|
||||
### Opening pull requests
|
||||
|
||||
- Before opening a pull request, check whether the account that will file it already has three open pull requests in this repository.
|
||||
- If so, alert the user that additional submissions may receive lower review priority and ask for explicit permission to proceed. Do not assume consent.
|
||||
|
||||
### Commits
|
||||
|
||||
- **Every commit you author must carry an `Assisted-by:` trailer** identifying the agent and whether it acted autonomously or under direct human supervision, for example:
|
||||
@@ -546,54 +471,4 @@ Disclosure is **continuous**, not a one-time event. A single AI-disclosure parag
|
||||
|
||||
---
|
||||
|
||||
## Error Handling and Debugging
|
||||
|
||||
### Common Errors and Fixes
|
||||
|
||||
| Symptom | Likely Cause | Fix |
|
||||
|---|---|---|
|
||||
| `Integration '<key>' not found` | Missing `_register()` call | Add `_register(<Name>Integration())` inside `_register_builtins()` |
|
||||
| `NameError: name '<Name>Integration' is not defined` at startup | Missing import | Add `from .<package_dir> import <Name>Integration` inside `_register_builtins()` |
|
||||
| CLI check fails for a `requires_cli: True` agent | `key` does not match the executable name | Set `key` to the exact name `shutil.which(key)` must resolve (e.g. `"cursor-agent"`, not `"cursor"`) |
|
||||
| Command files have the wrong argument syntax | Wrong `args` value in `registrar_config` | Use `$ARGUMENTS` for Markdown agents, `{{args}}` for TOML/YAML agents, or the agent's custom placeholder |
|
||||
| `ModuleNotFoundError` on a brand-new subpackage under pytest only | Ambient interpreter with a stale editable `.pth` | Run inside this tree's own venv (see Common Pitfall 6) |
|
||||
| Uninstall leaves files behind, or skips files you expected removed | Files not recorded via the manifest, or their hash changed after install | Route every created file through `manifest.record_file(...)`; user-edited files are intentionally skipped unless `force=True` |
|
||||
| Context file (`CLAUDE.md`, etc.) not updated | Expecting the CLI to manage it | Context files are owned by the opt-in `agent-context` extension, not the integration — see [Context file behavior](#4-context-file-behavior) |
|
||||
|
||||
### Debugging Tips
|
||||
|
||||
**Inspect the manifest** to see what an installed integration tracks:
|
||||
|
||||
```bash
|
||||
cat .specify/integrations/<key>.manifest.json
|
||||
```
|
||||
|
||||
**Verify a CLI tool is detected** before debugging a `requires_cli` agent:
|
||||
|
||||
```bash
|
||||
which <key> # Should print the executable path if installed
|
||||
```
|
||||
|
||||
**Verify the installed output structure** after `specify init`:
|
||||
|
||||
```bash
|
||||
find my-project/<folder> -type f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contribution Checklist
|
||||
|
||||
Before opening or merging an integration PR, confirm the following:
|
||||
|
||||
- [ ] Added the integration subpackage under `src/specify_cli/integrations/<package_dir>/`.
|
||||
- [ ] Registered it (import **and** `_register()`) in `src/specify_cli/integrations/__init__.py`, both alphabetical.
|
||||
- [ ] Added or updated tests in `tests/integrations/test_integration_<key>.py`.
|
||||
- [ ] Verified the install/uninstall flow with `specify init --integration <key>`.
|
||||
- [ ] Did **not** add `context_file` handling to the CLI (that belongs to the `agent-context` extension).
|
||||
- [ ] Updated devcontainer files if the agent needs a VS Code extension or CLI install step.
|
||||
- [ ] Updated this guide or other relevant docs if the integration has special setup or limitations.
|
||||
|
||||
---
|
||||
|
||||
*This documentation should be updated whenever new integrations are added to maintain accuracy and completeness.*
|
||||
|
||||
-390
@@ -2,396 +2,6 @@
|
||||
|
||||
<!-- insert new changelog below this comment -->
|
||||
|
||||
## [0.16.4] - 2026-08-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Add SpecAssay preset to community catalog (#4123)
|
||||
- Update Intake Authoring Governance preset to v0.3.1 (#4121)
|
||||
- Update Superspec extension to v1.0.2 (#4120)
|
||||
- fix(taskstoissues): widen task-ID regex to match IDs longer than 3 digits (#4101)
|
||||
- Add Architecture Governance extension to community catalog (#4122)
|
||||
- fix(workflows): validate non-string step types (#4111)
|
||||
- Harden community submission workflow output allowlists (#4103)
|
||||
- chore(deps): bump github/codeql-action (init + analyze) from 4.37.5 to 4.37.6 (#4114)
|
||||
- Add SpecAssay Check extension to community catalog (#4113)
|
||||
- fix(integrations): dispatch goose commands via `goose run` (#2416) (#3781)
|
||||
- fix(powershell): stop Out-Null swallowing the AVAILABLE_DOCS status lines (#3891)
|
||||
- fix: remove TOCTOU race in RunState.load (#3839)
|
||||
- fix: decode the zipped manifest as UTF-8 before parsing (#3958)
|
||||
- Update Agent Parity Governance preset to v0.4.2 (#4110)
|
||||
- fix: log progress tracker refresh errors instead of silently swallowing (#3975)
|
||||
- [extension] Add SpecJudge extension to community catalog (#4079)
|
||||
- fix(bundler): read the authoritative `default_integration` field, not only its legacy aliases (#3880)
|
||||
- fix(auth): treat exact host patterns literally (#4108)
|
||||
- feat: add Mistral Vibe integration with Claude parity (#4075)
|
||||
- [extension] Add spec-kit-atlas extension to community catalog (#4105)
|
||||
- chore: release 0.16.3, begin 0.16.4.dev0 development (#4107)
|
||||
|
||||
## [0.16.3] - 2026-08-13
|
||||
|
||||
### Changed
|
||||
|
||||
- fix: narrow bare except Exception in VS Code settings merge (#3844)
|
||||
- feat(presets): list presets in resolution/precedence order (#4086) (#4104)
|
||||
- Fix: scaffold self-contained namespaced preset commands (#4076) (#4082)
|
||||
- Update Cross-Platform Governance preset to v0.2.2 (#4080)
|
||||
- fix(bundler): treat a blank active integration as indeterminate in FR-019 (#3886)
|
||||
- Integrate Junie with dot-to-hyphen behavior and command formatting (#4073)
|
||||
- Update A11Y Governance preset to v0.4.3 (#4074)
|
||||
- Fix Alquimia argument hints after folded descriptions (#4063)
|
||||
- fix: use bounded read for bundle download HTTP responses (#3764)
|
||||
- Update iSAQB Architecture Governance preset to v0.2.2 (#4056)
|
||||
- fix(claude): make argument-hint injection fold-aware for long descriptions (#4045)
|
||||
- Add SpecKit Grill Me extension to community catalog (#4052)
|
||||
- Update Architecture Governance preset to v0.5.2 (#4050)
|
||||
- Remove auto-assign from catalog submission workflow (#4054)
|
||||
- docs: clarify example spec guidance (#4048)
|
||||
- Clarify custom checklist ownership and lifecycle (#4028)
|
||||
- Update Archive Extension to v1.2.2 (#4053)
|
||||
- Update Security Governance preset to v0.6.2 (#4040)
|
||||
- docs: clarify maintainer applies submission label during triage (#4041)
|
||||
- chore: release 0.16.2, begin 0.16.3.dev0 development (#4038)
|
||||
|
||||
## [0.16.2] - 2026-08-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Add Command Code integration to spec-kit (#4019)
|
||||
- fix(workflows): strip a resolved condition before the true/false check (#3883)
|
||||
- fix(workflows): guard a non-string overlay edit 'operation' (#3881)
|
||||
- fix: bound response read in integration catalog fetch (#3818)
|
||||
- Fix bug-test Python dependency provisioning (#4030)
|
||||
- fix(bundle): escape Rich markup in bundle CLI error and status output (#4023)
|
||||
- fix(presets): skip an unreadable restore source in `preset remove` (#4020)
|
||||
- Add Keel Discovery extension to community catalog (#4035)
|
||||
- fix: show error details in preset catalog config read failure (#3840)
|
||||
- Update Reconcile Extension to v1.1.0 (#4034)
|
||||
- Add Model Routing Governance preset to community catalog (#4033)
|
||||
- fix: use missing_ok=True in integration JSON removal (#3846)
|
||||
- fix: use missing_ok=True in extension cache clear (#3845)
|
||||
- fix(extensions): reject duplicate provides.templates/scripts names (#4016)
|
||||
- feat(presets): resolve constitution templates at command time (#3984)
|
||||
- [bug-fix] Fix preset-wrap-drops-argument-hint: inherit argument-hint from core template (#3996)
|
||||
- docs: document installing specify-cli from a custom package index (#4032)
|
||||
- feat(extensions): accept provides.templates and provides.scripts in manifest (#4012)
|
||||
- fix(presets): treat an unreadable core template as missing (#3961)
|
||||
- chore: release 0.16.1, begin 0.16.2.dev0 development (#4014)
|
||||
|
||||
## [0.16.1] - 2026-08-07
|
||||
|
||||
### Changed
|
||||
|
||||
- fix(integrations): wrap a non-UTF-8 catalog response (#4011)
|
||||
- fix(events): skip an unreadable command template (#3956)
|
||||
- fix(bundle): wrap malformed YAML in a local .zip bundle manifest (#4013)
|
||||
- chore(deps): bump github/codeql-action/analyze from 4.37.3 to 4.37.5 (#4005)
|
||||
- chore(deps): bump DavidAnson/markdownlint-cli2-action (#4006)
|
||||
- fix(agent-context): recurse for nested plans in Python mtime fallback (#3757)
|
||||
- fix: add utf-8 encoding to extension and preset registry file I/O (#3834)
|
||||
- fix(init): escape user-supplied values in `specify init` output (#3787)
|
||||
- fix: bound response read in integration catalog fetch (#3812)
|
||||
- fix: use missing_ok for temp file cleanup to avoid masking errors (#3803)
|
||||
- fix(workflows): handle an unreadable run state in `workflow status` (#3999)
|
||||
- feat(init): scaffold managed .specify/.gitignore (#4000)
|
||||
- fix(scripts): stop check-prerequisites text mode crashing on a legacy stdout code page (#3890)
|
||||
- fix(presets): return None for an unreadable layer in resolve_content (#3959)
|
||||
- fix(extensions): start fresh on a non-UTF-8 extension registry (#3998)
|
||||
- Fix init-force-preset-desync: reapply presets/extensions on init --here --force (#3995)
|
||||
- fix(skills): apply the line-anchored delimiter scan to hermes and kimi (#3739)
|
||||
- fix(archives): wrap the bare EOFError a truncated tar.gz raises (#3938)
|
||||
- test(integrations): guard multiline/control-char SKILL.md frontmatter escaping (#3392)
|
||||
- fix(scripts): stop setup-tasks text mode crashing on a legacy code page (#3892)
|
||||
- chore: release 0.16.0, begin 0.16.1.dev0 development (#3992)
|
||||
|
||||
## [0.16.0] - 2026-08-05
|
||||
|
||||
### Changed
|
||||
|
||||
- fix: keep long frontmatter values on a single line (#3989)
|
||||
- fix: skip corrupted run state files in list_runs (#3817)
|
||||
- fix: skip corrupted run state files in list_runs (#3814)
|
||||
- Add July 2026 newsletter (#3987)
|
||||
- fix(presets): start fresh on a non-UTF-8 preset registry (#3955)
|
||||
- docs: clarify agent PR review prioritization (#3985)
|
||||
- fix(events): preserve a non-UTF-8 config.toml on hook install/teardown (#3963)
|
||||
- fix(extensions): treat an unreadable staged backup as a conflict (#3962)
|
||||
- fix(manifests): reject non-string requires.speckit_version (#3980)
|
||||
- fix(extensions): reject reinstall when a kept config cannot be read (#3960)
|
||||
- [extension] Update Charter extension to v0.5.1 (#3983)
|
||||
- fix(events): return None for an unparseable script command (#3957)
|
||||
- feat(events): context injection for opencode and JSON-envelope agent hooks (#3934)
|
||||
- Add TDD Extension to community catalog (#3982)
|
||||
- Update Archive Extension to v1.1.0 (#3981)
|
||||
- feat(copilot): default integration to skills (#3976)
|
||||
- fix(events): ignore non-UTF-8 event overrides (#3897)
|
||||
- fix: cap stdin read at 1 MiB to prevent DoS (#3857)
|
||||
- fix(workflows): reject mismatched run state IDs (#3899)
|
||||
- chore: release 0.15.2, begin 0.15.3.dev0 development (#3953)
|
||||
|
||||
## [0.15.2] - 2026-08-03
|
||||
|
||||
### Changed
|
||||
|
||||
- fix(presets): restore core skills instead of deleting them on preset remove (#3929)
|
||||
- fix(manifests): reject non-string metadata instead of crashing on it (#3943)
|
||||
- fix: narrow bare except Exception in invoke separator resolution (#3856)
|
||||
- fix(workflows): keep the init step's documented ignore_agent_tools default on an explicit null (#3889)
|
||||
- fix(kimi): preserve non-UTF-8 user skills (#3895)
|
||||
- fix(presets): tolerate non-UTF-8 legacy commands (#3896)
|
||||
- feat: allow overriding default init integration via SPECKIT_INTEGRATION_DEFAULT (#3952)
|
||||
- Add adrkit extension to community catalog (#3947)
|
||||
- feat(extensions): scaffold config templates on extension add/enable (#2000)
|
||||
- fix(events): skip non-UTF-8 extension manifests (#3900)
|
||||
- fix(workflows): fail a gate whose on_reject is not abort/skip/retry (#3888)
|
||||
- fix(presets): validate required manifest mappings (#3898)
|
||||
- fix: eliminate TOCTOU race in zip packaging (#3855)
|
||||
- fix(workflows): fail a fan-in step whose output is not a mapping (#3887)
|
||||
- fix(workflows): refetch non-UTF-8 catalog caches (#3901)
|
||||
- fix(bundler): wrap local catalog decode failures (#3902)
|
||||
- Add `--extension` flag to `specify init` for opting into extensions at init time (#3914)
|
||||
- fix: bound response reads in extension catalog and download (#3775)
|
||||
- fix(workflows): reject a retry gate whose verdict enum forbids the reset value (#3912)
|
||||
- chore: release 0.15.1, begin 0.15.2.dev0 development (#3913)
|
||||
|
||||
## [0.15.1] - 2026-07-31
|
||||
|
||||
### Changed
|
||||
|
||||
- fix: escape Rich markup in `workflow resolve` output (#3879)
|
||||
- chore(deps): bump actions/stale from 10.4.0 to 11.0.0 (#3877)
|
||||
- chore(deps): bump actions/setup-python from 6.3.0 to 7.0.0 (#3876)
|
||||
- feat: support tar archives for installs (#3874)
|
||||
- fix: eliminate TOCTOU race in file unlink calls (#3819)
|
||||
- fix(scripts): tolerate an unusable integration.json in the Python helper (#3785)
|
||||
- fix(catalogs): validate the port in the shared catalog-URL validator, like its mirrors do (#3804)
|
||||
- feat(presets): add opt-in constitution-sync preset (#3873)
|
||||
- fix: reject non-object workflow caches (#3860)
|
||||
- Harden extension URL download cache against symlink and junction races (#3869)
|
||||
- fix: escape workflow step metadata (#3863)
|
||||
- [bug-fix] Fix bundle-update-force-mislead: add refresh() to DefaultPrimitiveInstaller (#3452)
|
||||
- fix: use chunked read for extension manifest hash (#3841)
|
||||
- fix: preserve unreadable event config files (#3861)
|
||||
- fix(scripts): use a .NET Framework-safe trim in the PowerShell init-dir resolver (#3872)
|
||||
- Add ContextForge MCP extension to community catalog (#3487)
|
||||
- fix: normalize non-UTF-8 integration manifests (#3862)
|
||||
- feat: bind gate verdict to workflow input via verdict_input (#3725)
|
||||
- docs: use absolute image URLs in README for PyPI rendering (#3867)
|
||||
- chore: release 0.15.0, begin 0.15.1.dev0 development (#3871)
|
||||
|
||||
## [0.15.0] - 2026-07-30
|
||||
|
||||
### Changed
|
||||
|
||||
- Add yolo to community workflow catalog (#3864)
|
||||
- fix(workflows): guard the shell step's timeout check against OverflowError (#3865)
|
||||
- Add Intent Reconciliation extension to community catalog (#3858)
|
||||
- fix(workflows): validate prompt step 'timeout' like the shell step (#3847)
|
||||
- fix: add utf-8 encoding to registry file open calls (#3816)
|
||||
- fix: eliminate TOCTOU race in file unlink calls (#3815)
|
||||
- test(workflows): name the condition-rejection tests for the real boundary (#3808)
|
||||
- fix: eliminate TOCTOU race in file unlink calls (#3811)
|
||||
- fix(presets): escape user-supplied catalog name/URL in add/remove output (#3806)
|
||||
- fix: add missing utf-8 encoding to registry file open calls (#3810)
|
||||
- [bug-fix] Fix upgrade-overwrites-copilot-skills: pass force=True to extension skill re-registration after upgrade (#3853)
|
||||
- fix(integrations): don't abort uninstall when the manifest can't be deleted (#3805)
|
||||
- test(extensions): update stale manifest validation message assertion (#3859)
|
||||
- fix(agents): coerce a non-string description in TOML command rendering (#3799)
|
||||
- fix(workflows): make security requirements sync deterministic (#3832)
|
||||
- fix(cli): render the literal [suffix] in --tag help and rejection message (#3800)
|
||||
- fix(integrations): preserve non-UTF-8 VS Code settings (#3833)
|
||||
- fix(bundler): treat an explicit-null manifest field as missing, not the text "None" (#3798)
|
||||
- feat: first-class agent-native runtime hooks for integrations (#3704)
|
||||
- fix(extensions): guard the required manifest sections so one bad extension cannot break `extension list` (#3797)
|
||||
- fix(presets): escape installed preset metadata in Rich output (#3826)
|
||||
- fix(workflows): dispatch prompt steps via the resolved executable (#3793)
|
||||
- chore: release 0.14.4, begin 0.14.5.dev0 development (#3850)
|
||||
|
||||
## [0.14.4] - 2026-07-29
|
||||
|
||||
### Changed
|
||||
|
||||
- fix(bundler): degrade non-UTF-8 config reads into BundlerError (#3784)
|
||||
- fix(workflows): escape the step-progress line so step ids render (and `/` stops failing the run) (#3783)
|
||||
- Update Agent Parity Governance preset to v0.4.1 (#3830)
|
||||
- fix(integrations): reject empty --commands-dir in generic raw_options (#3714)
|
||||
- fix(presets): guard non-list/non-mapping provides.templates in PresetManifest (#3712)
|
||||
- fix(auth): resolve az via shutil.which so azure-cli token works on Windows (#3709)
|
||||
- fix(workflows): reject falsy non-mapping workflow-catalogs.yml top level (#3707)
|
||||
- fix(integrations): render hyphenated /speckit-<name> for Droid (always-slash agent) (#3688)
|
||||
- [preset] Update A11Y Governance preset to v0.4.2 (#3828)
|
||||
- [preset] Update Parallel Autonomous Run Governance to v0.2.4 (#3825)
|
||||
- fix: correct Optional type annotation for _resolved_dir parameter (#3801)
|
||||
- fix: add timeout to prompt step subprocess execution (#3768)
|
||||
- fix: handle tags containing / in GitHub release asset URL resolution (#3767)
|
||||
- fix(presets): escape catalog metadata in discovery output (#3773)
|
||||
- Update Autonomous Run Governance preset to v0.3.3 (#3823)
|
||||
- fix: use bounded read for integration catalog HTTP responses (#3763)
|
||||
- docs: add Simplified Chinese translation of README (#3740)
|
||||
- Update Intake Sequencing Governance preset to v0.2.2 (#3809)
|
||||
- fix(workflows): reject non-string/non-boolean 'condition' in if/while/do-while steps (#3706)
|
||||
- fix(bundle): escape catalog metadata in discovery output (#3774)
|
||||
- fix(workflows,extensions): tolerate non-list catalog tags in search/info display (#3770)
|
||||
- fix: correct nullable resolved directory annotation (#3771)
|
||||
- fix(presets): tolerate non-string and non-list catalog fields in preset search/info (#3769)
|
||||
- fix(integrations): escape catalog metadata in discovery output (#3772)
|
||||
- Update Verify Review Ship extension to v0.4.2 (#3792)
|
||||
- fix(integrations): preserve native skill invocation prefixes (#3663)
|
||||
- Update Intake Review Governance preset to v0.2.0 (#3796)
|
||||
- fix(constitution): stop propagating guidance into templates (#3737) (#3790)
|
||||
- chore: release 0.14.3, begin 0.14.4.dev0 development (#3795)
|
||||
|
||||
## [0.14.3] - 2026-07-28
|
||||
|
||||
### Changed
|
||||
|
||||
- Update Intake Authoring Governance preset to v0.3.0 (#3788)
|
||||
- fix(copilot): honor preset command template overrides (#3592)
|
||||
- clarify: require real interrogatives, ban topic-label questions (#3745)
|
||||
- feat: Add Alquimia AI integration (#2734)
|
||||
- harden: secure extension and preset archive downloads (#3141)
|
||||
- fix: correct Optional type annotation for context_note parameter (#3765)
|
||||
- Update AGENTS.md (#2626)
|
||||
- fix(extensions): tolerate non-string catalog name in display-name lookup (#3747)
|
||||
- fix(presets): coerce non-string catalog tags before joining (#3743)
|
||||
- fix: register extensions for the active integration only (#3459)
|
||||
- fix(extensions): tolerate non-string tags in catalog search (#3746)
|
||||
- fix(extensions): hyphenate command names in 'extension info' listing (#3744)
|
||||
- fix(workflows): escape remaining untrusted fields in `workflow info` (#3731)
|
||||
- fix(extensions): guard non-numeric catalog downloads in search/info rendering (#3710)
|
||||
- fix(agent-context): apply default markers when config markers are blank (bash) (#3736)
|
||||
- fix: escape Rich markup in catalog list output (#3738)
|
||||
- fix(workflows): guard non-mapping 'workflow:' block in WorkflowDefinition (#3694)
|
||||
- fix(bundler): reject unsupported schema_version in _merge_config (align readers) (#3711)
|
||||
- Update Linear Weave extension to v1.0.1 (#3762)
|
||||
- Add Intake Sequencing Governance preset to community catalog (#3761)
|
||||
- Update Quality Gates (Enforcement Layer) extension to v0.3.3 (#3760)
|
||||
- Update Verify Review Ship extension to v0.4.1 (#3759)
|
||||
- fix(agent-context): discover nested plans in Python port mtime fallback (#3734)
|
||||
- fix(extensions): make shipped scripts executable after install (#3723)
|
||||
- docs(assess): clarify the pipeline works on an empty project (#3732)
|
||||
- chore: release 0.14.2, begin 0.14.3.dev0 development (#3730)
|
||||
|
||||
## [0.14.2] - 2026-07-24
|
||||
|
||||
### Changed
|
||||
|
||||
- Update Intake Review Governance preset to v0.1.1 (#3729)
|
||||
- Update Verify Review Ship extension to v0.3.0 (#3728)
|
||||
- Update Architecture Guard extension to v1.13.1 (#3724)
|
||||
- docs(upgrade): Claude Code files live in .claude/skills, not .claude/commands (#3708)
|
||||
- fix(kilocode): install commands under .kilo/commands (#3672)
|
||||
- fix(auth): normalize whitespace in auth-config env-var/id references at store time (#3691)
|
||||
- fix(workflows): guard non-mapping 'inputs:' block in engine._resolve_inputs (#3696)
|
||||
- Update Intake Authoring Governance preset to v0.2.0 (#3721)
|
||||
- docs: clarify shell-step interpolation safety (#3719)
|
||||
- [extension] Add Blueprint Index — Living Architecture Map extension to community catalog (#3718)
|
||||
- fix(github-http): return None on malformed host in resolve_github_release_asset_api_url (#3715)
|
||||
- fix(integrations): declare PiIntegration multi_install_safe (#3652)
|
||||
- harden: remove shell parameter from run_command() (#3716)
|
||||
- chore(deps): bump github/codeql-action/init from 4.37.1 to 4.37.3 (#3699)
|
||||
- fix: auto-correct conflicting feature prefixes (#1829)
|
||||
- chore(deps): bump actions/checkout from 6.0.3 to 7.0.1 (#3703)
|
||||
- chore(deps): bump DavidAnson/markdownlint-cli2-action (#3702)
|
||||
- chore(deps): bump actions/setup-node from 6.4.0 to 7.0.0 (#3701)
|
||||
- chore(deps): bump astral-sh/setup-uv from 8.3.2 to 9.0.0 (#3700)
|
||||
- chore: release 0.14.1, begin 0.14.2.dev0 development (#3698)
|
||||
|
||||
## [0.14.1] - 2026-07-23
|
||||
|
||||
### Changed
|
||||
|
||||
- Update Agent Parity Governance preset to v0.4.0 (#3697)
|
||||
- fix(bundler): InstallResult.changed counts uninstalled as a change (#3692)
|
||||
- [preset] Update Cross-Platform Governance preset to v0.2.1 (#3695)
|
||||
- Update A11Y Governance preset to v0.4.1 (#3693)
|
||||
- fix(workflows): escape step-graph brackets in `workflow info` so the type shows (#3690)
|
||||
- fix(workflows): filter parser rejects trailing tokens (fullmatch, not match) (#3689)
|
||||
- Update iSAQB Architecture Governance preset to v0.2.1 (#3687)
|
||||
- fix(extensions): parse SKILL.md on the --- delimiter line during removal (#3634)
|
||||
- fix(cli): guard lazy .hostname ValueError in extension/preset add --from (#3651)
|
||||
- Update Architecture Governance preset to v0.5.1 (#3686)
|
||||
- fix(bundler): reject a top-level non-mapping bundle-catalogs.yml in _merge_config (#3659)
|
||||
- Update Security Governance preset to v0.6.1 (#3685)
|
||||
- fix(integrations): declare OmpIntegration multi_install_safe (#3650)
|
||||
- feat(git-extension): add configurable Conventional Commit support (#3390) (#3413)
|
||||
- fix(extensions): hyphenate command names in the Forge post-install listing (#3669)
|
||||
- fix(bundler): reject falsy non-mapping requires/provides in CatalogEntry.from_dict (#3667)
|
||||
- fix(bundler): reject falsy non-list bundles/contributed_components in records (#3666)
|
||||
- Update Intake Authoring Governance preset to v0.1.1 (#3678)
|
||||
- docs(extensions): clarify agent-context README and add config examples (#3389)
|
||||
- chore: release 0.14.0, begin 0.14.1.dev0 development (#3677)
|
||||
|
||||
## [0.14.0] - 2026-07-23
|
||||
|
||||
### Changed
|
||||
|
||||
- docs: add spec-kit-copilot to community friends (#3675)
|
||||
- fix(integrations): recompute invoke_separator from retained parsed_options (#3664)
|
||||
- fix(workflows): preserve intra-overlay order for multiple insert_after edits (#3662)
|
||||
- fix(bundler): reject falsy non-mapping requires/provides in manifest from_dict (#3661)
|
||||
- fix(bundler): dump_yaml writes literal UTF-8 (allow_unicode=True) (#3660)
|
||||
- fix(integrations): declare kiro-cli multi-install safe (#3477)
|
||||
- fix(git-extension): trim trailing whitespace before stripping commit-message quotes (#3673)
|
||||
- fix(bundler): order bundle members by canonical POSIX arcname (reproducible builds) (#3658)
|
||||
- fix(integrations): Cline overrides post_process_command_content (correct hook name) (#3657)
|
||||
- docs(workflows): gate step docstring lists the 'retry' on_reject behaviour (#3656)
|
||||
- fix: harden bounded reads and redirect validation (#3671)
|
||||
- fix(packaging): bundle scripts/python into the wheel core_pack (#3665) (#3670)
|
||||
- fix: bundle scripts/python in wheel so --script py works (#3665) (#3668)
|
||||
- docs(workflows): init step docstring lists the 'py' script type (#3655)
|
||||
- fix(integrations): declare LingmaIntegration multi_install_safe (#3654)
|
||||
- fix: guard constitution command against feature execution (#3646)
|
||||
- Fix duplicate step numbering in specify command (#3647)
|
||||
- docs(scripts): document the 'py' script type and sh/ps migration plan (#3284) (#3653)
|
||||
- harden: bound HTTP reads and enforce strict redirects (#3140)
|
||||
- chore: release 0.13.4, begin 0.13.5.dev0 development (#3649)
|
||||
|
||||
## [0.13.4] - 2026-07-22
|
||||
|
||||
### Changed
|
||||
|
||||
- docs(concepts): document the spec-of-specs feature breakdown approach (#3648)
|
||||
- fix(scripts): git-ext PowerShell emits the '# To persist' SPECIFY_FEATURE hint (parity) (#3632)
|
||||
- fix(integrations): validate cached catalog shape before returning it (#3627)
|
||||
- fix(bundler): reject non-list 'catalogs' in bundle-catalogs.yml with a clean error (#3623)
|
||||
- fix(bundler): guard lazy .hostname ValueError in catalog add_source (#3644)
|
||||
- Add Intake Authoring Governance preset to community catalog (#3643)
|
||||
- feat: add Factory Droid CLI integration (#822) (#3587)
|
||||
- docs(installation): document the 'py' (Python) script type (#3640)
|
||||
- fix(init): show hyphenated /speckit-<name> in Next Steps for Forge projects (#3642)
|
||||
- fix(extensions): render hyphenated hook invocations for Forge projects (#3641)
|
||||
- fix(workflows): workflow add detects local YAML files case-insensitively (#3633)
|
||||
- fix(workflows): list-literal expression ignores trailing/empty commas (#3631)
|
||||
- fix(workflows): StepRegistry.add tolerates a corrupted non-dict existing entry (#3630)
|
||||
- fix(bundler): reject non-mapping 'integration' in a bundle manifest (#3629)
|
||||
- fix(workflows): command/prompt steps fail cleanly on a non-string integration (#3626)
|
||||
- docs(core): document the 'py' (Python) --script type in the init option table (#3625)
|
||||
- fix(workflows): gate prompt uses isdecimal() so a superscript digit doesn't crash (#3624)
|
||||
- fix(integrations): Cline dispatches hyphenated /speckit-<cmd> invocations (#3622)
|
||||
- docs(upgrade): document integration upgrade / extension update as the project-files upgrade path (#3326)
|
||||
- chore: release 0.13.3, begin 0.13.4.dev0 development (#3645)
|
||||
|
||||
## [0.13.3] - 2026-07-22
|
||||
|
||||
### Changed
|
||||
|
||||
- fix(integrations): escape Rich markup in --integration-options error messages (#3458)
|
||||
- docs: document __SPECKIT_COMMAND_ token for portable cross-command references (#3503)
|
||||
- [preset] Add Parallel Autonomous Run Governance preset to community catalog (#3614)
|
||||
- docs(workflows): fix stale FanOutStep docstring claiming sequential-only execution (#3639)
|
||||
- [bundle] Add SicarioSpec Security & Governance Bundle to community catalog (#3636)
|
||||
- [preset] Update Autonomous Run Governance preset to v0.3.2 (#3615)
|
||||
- fix(workflows): validate every redirect hop when fetching workflow/step catalogs (#3637)
|
||||
- Add pipeline workflow to community catalog (#3338)
|
||||
- [extension] Add Linear Weave extension to community catalog (#3609)
|
||||
- docs: clarify hook priority validation semantics (#3594)
|
||||
- fix(workflows): reject a non-string 'integration'/'model' in command & prompt steps (#3597)
|
||||
- ci: add dependency audit workflow (#3138)
|
||||
- Add Intake Review Governance preset to community catalog (#3613)
|
||||
- fix(workflows): reject non-list input 'enum' instead of crashing (#3601)
|
||||
- chore: release 0.13.2, begin 0.13.3.dev0 development (#3617)
|
||||
|
||||
## [0.13.2] - 2026-07-21
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -55,8 +55,6 @@ Here are a few things you can do that will increase the likelihood of your pull
|
||||
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
- Test your changes with the Spec-Driven Development workflow to ensure compatibility.
|
||||
|
||||
Accounts with three open pull requests may continue submitting changes, but additional submissions may be placed behind contributions from other authors in the review queue. Coding agents should disclose this possibility and obtain the filer's confirmation before opening another pull request.
|
||||
|
||||
### Branch naming
|
||||
|
||||
We recommend naming branches as `<type>/<number>-<short-slug>`, where `<number>` is the issue or PR number (whichever comes first) and `<type>` is one of:
|
||||
@@ -115,27 +113,6 @@ uv pip install -e ".[test]"
|
||||
> `specify_cli` to this checkout's `src/`. This matches the gotcha documented in
|
||||
> `AGENTS.md` (Common Pitfalls).
|
||||
|
||||
#### Security checks
|
||||
|
||||
```bash
|
||||
uvx --from pip-audit==2.10.0 pip-audit --disable-pip --require-hashes -r .github/security-audit-requirements.txt --progress-spinner off
|
||||
```
|
||||
|
||||
This command audits the committed hashed requirements snapshot. Pull request,
|
||||
push, and manual CI runs use the same snapshot so their results stay
|
||||
deterministic. If dependency metadata changes, refresh and commit the snapshot
|
||||
before auditing it:
|
||||
|
||||
```bash
|
||||
uv pip compile pyproject.toml --extra test --universal --upgrade --generate-hashes --quiet --no-header --output-file .github/security-audit-requirements.txt
|
||||
```
|
||||
|
||||
The scheduled CI audit resolves the runtime and `test` extra dependency set
|
||||
across the supported Python and OS matrix to catch newly published advisories.
|
||||
Upstream package releases drift over time, so even an unrelated PR touching
|
||||
`pyproject.toml` can fail the `dependency-audit` check until the committed file
|
||||
is regenerated with the command above and re-committed.
|
||||
|
||||
#### Shell scripts
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/github/spec-kit/main/media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
|
||||
<img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
|
||||
<h1>🌱 Spec Kit</h1>
|
||||
<h3><em>Define what to build before building it — with any AI coding agent.</em></h3>
|
||||
</div>
|
||||
@@ -15,11 +15,6 @@
|
||||
<a href="https://github.github.io/spec-kit/"><img src="https://img.shields.io/badge/docs-GitHub_Pages-blue" alt="Documentation"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>English</strong> ·
|
||||
<a href="./README.zh-CN.md">简体中文</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
@@ -90,7 +85,7 @@ Bare `specify self upgrade` executes immediately, matching the no-prompt behavio
|
||||
|
||||
### 3. Establish project principles
|
||||
|
||||
Launch your coding agent in the project directory. Most agents expose spec-kit as `/speckit.*` slash commands; Codex CLI and Command Code in skills mode use `$speckit-*` instead; GitHub Copilot CLI uses `/agents` to select the agent or address it directly in a prompt.
|
||||
Launch your coding agent in the project directory. Most agents expose spec-kit as `/speckit.*` slash commands; Codex CLI in skills mode uses `$speckit-*` instead; GitHub Copilot CLI uses `/agents` to select the agent or address it directly in a prompt.
|
||||
|
||||
Use the **`/speckit.constitution`** command to create your project's governing principles and development guidelines that will guide all subsequent development.
|
||||
|
||||
@@ -136,7 +131,7 @@ For detailed step-by-step instructions, see our [comprehensive guide](./spec-dri
|
||||
|
||||
Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)!
|
||||
|
||||
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
||||
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
||||
|
||||
## 🌍 Community
|
||||
|
||||
@@ -283,11 +278,9 @@ specify bundle validate --path ./my-bundle # structural + reference checks
|
||||
specify bundle build --path ./my-bundle # produce a versioned .zip artifact
|
||||
```
|
||||
|
||||
Four ready-to-read example bundle manifests live under
|
||||
Four ready-to-read example manifests live under
|
||||
[`examples/bundles/`](examples/bundles/) (product manager, business analyst,
|
||||
security researcher, developer). These are bundle packaging examples, not
|
||||
filled generated feature specs; for end-to-end community examples, see the
|
||||
[community walkthroughs](https://github.github.io/spec-kit/community/walkthroughs.html).
|
||||
security researcher, developer).
|
||||
|
||||
Key guarantees: `info` shows exactly what `install` adds (transparency);
|
||||
installs are idempotent and confined to the project root; `remove` never touches
|
||||
|
||||
-361
@@ -1,361 +0,0 @@
|
||||
<div align="center">
|
||||
<img src="./media/logo_large.webp" alt="Spec Kit Logo" width="200" height="200"/>
|
||||
<h1>🌱 Spec Kit</h1>
|
||||
<h3><em>在动手编码之前,先定义要构建什么 —— 适配任意 AI 编码助手。</em></h3>
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<strong>一个开源工具套件,帮助你借助任意 AI 编码助手构建高质量软件 —— 内置开箱即用的规范驱动流程(也可自带流程),可无限扩展、由社区驱动,并为整个组织的协作而设计。</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/github/spec-kit/releases/latest"><img src="https://img.shields.io/github/v/release/github/spec-kit" alt="Latest Release"/></a>
|
||||
<a href="https://github.com/github/spec-kit/stargazers"><img src="https://img.shields.io/github/stars/github/spec-kit?style=social" alt="GitHub stars"/></a>
|
||||
<a href="https://github.com/github/spec-kit/blob/main/LICENSE"><img src="https://img.shields.io/github/license/github/spec-kit" alt="License"/></a>
|
||||
<a href="https://github.github.io/spec-kit/"><img src="https://img.shields.io/badge/docs-GitHub_Pages-blue" alt="Documentation"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="./README.md">English</a> ·
|
||||
<strong>简体中文</strong>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [🤔 什么是规范驱动开发?](#-什么是规范驱动开发)
|
||||
- [⚡ 快速开始](#-快速开始)
|
||||
- [📽️ 视频概览](#️-视频概览)
|
||||
- [🌍 社区](#-社区)
|
||||
- [🤖 支持的 AI 编码助手集成](#-支持的-ai-编码助手集成)
|
||||
- [🔧 Specify CLI 参考](#-specify-cli-参考)
|
||||
- [🧩 打造你自己的 Spec Kit:扩展与预设](#-打造你自己的-spec-kit扩展与预设)
|
||||
- [📦 捆绑包:面向角色的一键配置](#-捆绑包面向角色的一键配置)
|
||||
- [📚 核心理念](#-核心理念)
|
||||
- [🌟 开发阶段](#-开发阶段)
|
||||
- [🎯 实验目标](#-实验目标)
|
||||
- [🔧 环境要求](#-环境要求)
|
||||
- [📖 深入了解](#-深入了解)
|
||||
- [💬 支持](#-支持)
|
||||
- [🙏 致谢](#-致谢)
|
||||
- [📄 许可证](#-许可证)
|
||||
|
||||
## 🤔 什么是规范驱动开发?
|
||||
|
||||
规范驱动开发(Spec-Driven Development)**颠覆了**传统软件开发的思路。几十年来,代码一直是核心 —— 规范只是编码这项"正事"开始前搭起、随后就被丢弃的脚手架。规范驱动开发改变了这一点:**规范本身变得可执行**,它不再只是引导实现,而是直接生成可运行的实现。
|
||||
|
||||
## ⚡ 快速开始
|
||||
|
||||
### 1. 安装 Specify CLI
|
||||
|
||||
需要 **[uv](https://docs.astral.sh/uv/)**([安装 uv](./docs/install/uv.md))。将 `vX.Y.Z` 替换为 [Releases](https://github.com/github/spec-kit/releases) 中最新的发布标签 —— 记得保留开头的 `v`(例如 `v0.12.11`,而不是 `0.12.11`):
|
||||
|
||||
```bash
|
||||
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@vX.Y.Z
|
||||
```
|
||||
|
||||
更倾向从 PyPI 安装?`specify-cli` 包同样发布在那里:
|
||||
|
||||
```bash
|
||||
uv tool install specify-cli
|
||||
```
|
||||
|
||||
其他安装方式、安装校验、升级以及故障排查,请参阅[安装指南](./docs/installation.md)。
|
||||
|
||||
### 2. 初始化项目
|
||||
|
||||
```bash
|
||||
specify init my-project --integration copilot
|
||||
cd my-project
|
||||
```
|
||||
|
||||
要检查更新或升级已安装的 CLI,可使用自管理命令。更详细的场景和自定义选项请参阅[升级指南](./docs/upgrade.md)。
|
||||
|
||||
```bash
|
||||
# 检查是否有更新版本可用(只读操作 —— 不会修改任何内容)
|
||||
specify self check
|
||||
|
||||
# 预览升级将执行的操作,但不实际升级
|
||||
specify self upgrade --dry-run
|
||||
|
||||
# 就地升级到最新稳定版(自动识别 uv tool 与 pipx 安装方式)
|
||||
specify self upgrade
|
||||
|
||||
# 或锁定到指定的发布标签(将 vX.Y.Z[suffix] 替换为你想要的标签)
|
||||
specify self upgrade --tag vX.Y.Z[suffix]
|
||||
```
|
||||
|
||||
直接运行 `specify self upgrade` 会立即执行,与 `pip install -U`、`npm update` 等命令一样无需额外确认。对于 `uv tool` 安装的情况,它在底层会执行 `uv tool install specify-cli --force --from <git ref>`,因此锁定的发布标签同样有效,包括 dev、alpha/beta/rc 或带构建元数据的后缀。`uvx`(临时运行)和源码检出会被自动识别,此时会给出针对具体路径的操作建议,而不会执行安装程序。可通过设置 `SPECIFY_UPGRADE_TIMEOUT_SECS` 来限制安装子进程的最长运行时间(默认无超时限制 —— 必要时用 `Ctrl+C` 中断)。
|
||||
|
||||
### 3. 确立项目准则
|
||||
|
||||
在项目目录下启动你的编码助手。大多数助手将 spec-kit 暴露为 `/speckit.*` 斜杠命令;处于技能(skills)模式的 Codex CLI 则使用 `$speckit-*`;GitHub Copilot CLI 使用 `/agents` 来选择助手,或直接在提示词中指定它。
|
||||
|
||||
使用 **`/speckit.constitution`** 命令来创建项目的治理准则和开发指南,它们将指导后续所有开发工作。
|
||||
|
||||
```bash
|
||||
/speckit.constitution Create principles focused on code quality, testing standards, user experience consistency, and performance requirements
|
||||
```
|
||||
|
||||
### 4. 编写规范
|
||||
|
||||
使用 **`/speckit.specify`** 命令描述你想构建什么。聚焦于**做什么**和**为什么做**,而不是技术栈。
|
||||
|
||||
```bash
|
||||
/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.
|
||||
```
|
||||
|
||||
### 5. 制定技术实现方案
|
||||
|
||||
使用 **`/speckit.plan`** 命令提供你的技术栈和架构选择。
|
||||
|
||||
```bash
|
||||
/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.
|
||||
```
|
||||
|
||||
### 6. 拆解为任务
|
||||
|
||||
使用 **`/speckit.tasks`** 从实现方案生成一份可执行的任务清单。
|
||||
|
||||
```bash
|
||||
/speckit.tasks
|
||||
```
|
||||
|
||||
### 7. 执行实现
|
||||
|
||||
使用 **`/speckit.implement`** 执行所有任务,按方案构建你的功能。
|
||||
|
||||
```bash
|
||||
/speckit.implement
|
||||
```
|
||||
|
||||
详细的分步说明,请参阅我们的[完整指南](./spec-driven.md)。
|
||||
|
||||
## 📽️ 视频概览
|
||||
|
||||
想看看 Spec Kit 的实际效果?观看我们的[视频概览](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)!
|
||||
|
||||
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
||||
|
||||
## 🌍 社区
|
||||
|
||||
在 [Spec Kit 文档站点](https://github.github.io/spec-kit/)上探索由社区贡献的资源:
|
||||
|
||||
- [扩展(Extensions)](https://github.github.io/spec-kit/community/extensions.html) —— 命令、钩子与各类能力
|
||||
- [预设(Presets)](https://github.github.io/spec-kit/community/presets.html) —— 模板与术语覆盖
|
||||
- [捆绑包(Bundles)](https://github.github.io/spec-kit/community/bundles.html) —— 由现有组件组合而成的角色与团队技术栈
|
||||
- [实战演练(Walkthroughs)](https://github.github.io/spec-kit/community/walkthroughs.html) —— 端到端的 SDD 场景
|
||||
- [伙伴项目(Friends)](https://github.github.io/spec-kit/community/friends.html) —— 扩展 Spec Kit 或基于它构建的项目
|
||||
|
||||
> [!NOTE]
|
||||
> 社区贡献由各自的作者独立创建和维护。请在安装前审阅源代码,并自行斟酌使用。
|
||||
|
||||
想要参与贡献?请参阅[扩展发布指南](extensions/EXTENSION-PUBLISHING-GUIDE.md)、[预设发布指南](presets/PUBLISHING.md)或[社区捆绑包指南](docs/community/bundles.md)。
|
||||
|
||||
## 🤖 支持的 AI 编码助手集成
|
||||
|
||||
Spec Kit 可与 30 多个 AI 编码助手协作 —— 既包括 CLI 工具,也包括基于 IDE 的助手。完整列表以及相关说明和使用细节,请参阅[支持的 AI 编码助手集成](https://github.github.io/spec-kit/reference/integrations.html)指南。
|
||||
|
||||
运行 `specify integration list` 可查看当前安装版本中所有可用的集成。
|
||||
|
||||
## 可用的斜杠命令
|
||||
|
||||
运行 `specify init` 后,你的 AI 编码助手就能使用这些斜杠命令来进行结构化开发。对于支持技能模式的集成,传入 `--integration <agent> --integration-options="--skills"` 会安装助手技能,而不是斜杠命令的提示词文件。
|
||||
|
||||
### 核心命令
|
||||
|
||||
规范驱动开发工作流中必不可少的命令:
|
||||
|
||||
| 命令 | 助手技能 | 说明 |
|
||||
| ------------------------ | ---------------------- | ---------------------------------------------------------- |
|
||||
| `/speckit.constitution` | `speckit-constitution` | 创建或更新项目的治理准则和开发指南 |
|
||||
| `/speckit.specify` | `speckit-specify` | 定义你想构建什么(需求与用户故事) |
|
||||
| `/speckit.plan` | `speckit-plan` | 结合所选技术栈制定技术实现方案 |
|
||||
| `/speckit.tasks` | `speckit-tasks` | 生成可执行的实现任务清单 |
|
||||
| `/speckit.taskstoissues` | `speckit-taskstoissues`| 将生成的任务清单转换为 GitHub issue,便于跟踪与执行 |
|
||||
| `/speckit.implement` | `speckit-implement` | 执行所有任务,按方案构建功能 |
|
||||
| `/speckit.converge` | `speckit-converge` | 对照规范/方案/任务评估代码库,并将剩余工作追加为新任务 |
|
||||
|
||||
### 可选命令
|
||||
|
||||
用于提升质量与做校验的额外命令:
|
||||
|
||||
| 命令 | 助手技能 | 说明 |
|
||||
| -------------------- | ---------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `/speckit.clarify` | `speckit-clarify` | 澄清描述不充分的部分(建议在 `/speckit.plan` 之前使用;旧称 `/quizme`) |
|
||||
| `/speckit.analyze` | `speckit-analyze` | 跨制品的一致性与覆盖度分析(在 `/speckit.tasks` 之后、`/speckit.implement` 之前运行) |
|
||||
| `/speckit.checklist` | `speckit-checklist` | 生成自定义质量清单,校验需求的完整性、清晰度与一致性(好比"为自然语言写单元测试") |
|
||||
|
||||
## 🔧 Specify CLI 参考
|
||||
|
||||
完整的命令详情、选项与示例,请参阅 [CLI 参考文档](https://github.github.io/spec-kit/reference/overview.html)。
|
||||
|
||||
## 🧩 打造你自己的 Spec Kit:扩展与预设
|
||||
|
||||
Spec Kit 可通过两套互补的机制进行深度定制 —— **扩展(extensions)** 和 **预设(presets)** —— 以及面向单个项目的本地覆盖,用于临时性调整:
|
||||
|
||||
| 优先级 | 组件类型 | 位置 |
|
||||
| -----: | ---------------------------------- | -------------------------------- |
|
||||
| ⬆ 1 | 项目本地覆盖 | `.specify/templates/overrides/` |
|
||||
| 2 | 预设 —— 定制核心与扩展 | `.specify/presets/templates/` |
|
||||
| 3 | 扩展 —— 新增能力 | `.specify/extensions/templates/` |
|
||||
| ⬇ 4 | Spec Kit 核心 —— 内置 SDD 命令与模板 | `.specify/templates/` |
|
||||
|
||||
- **模板**在**运行时**解析 —— Spec Kit 从高到低遍历优先级栈,使用第一个匹配项。
|
||||
- 项目本地覆盖(`.specify/templates/overrides/`)允许对单个项目做一次性调整,无需创建完整的预设。
|
||||
- **扩展/预设命令**在**安装时**生效 —— 当你运行 `specify extension add` 或 `specify preset add` 时,命令文件会被写入助手目录(如 `.claude/commands/`)。
|
||||
- 若多个预设或扩展提供了同一命令,优先级最高的版本生效。移除时,次优先级的版本会自动恢复。
|
||||
- 若不存在任何覆盖或自定义,Spec Kit 使用核心默认配置。
|
||||
|
||||
### 扩展 —— 新增能力
|
||||
|
||||
当你需要 Spec Kit 核心之外的功能时,使用**扩展**。扩展可引入新命令和模板 —— 例如添加核心 SDD 命令未覆盖的领域特定工作流、集成外部工具,或新增全新的开发阶段。它们扩展了 *Spec Kit 能做什么*。
|
||||
|
||||
```bash
|
||||
# 搜索可用扩展
|
||||
specify extension search
|
||||
|
||||
# 安装扩展
|
||||
specify extension add <extension-name>
|
||||
```
|
||||
|
||||
举例来说,扩展可以添加 Jira 集成、实现后代码审查、V 模型测试追溯性,或项目健康诊断等功能。
|
||||
|
||||
完整命令指南请参阅[扩展参考文档](https://github.github.io/spec-kit/reference/extensions.html)。浏览[社区扩展](https://github.github.io/spec-kit/community/extensions.html)了解现有资源。
|
||||
|
||||
### 预设 —— 定制现有工作流
|
||||
|
||||
当你想改变 Spec Kit 的*工作方式*而不是新增能力时,使用**预设**。预设会覆盖核心及已安装扩展中附带的模板和命令 —— 例如强制使用面向合规的规范格式、采用领域特定术语,或对方案和任务应用组织规范。预设定制的是 Spec Kit 及其扩展生成的制品与指令。
|
||||
|
||||
```bash
|
||||
# 搜索可用预设
|
||||
specify preset search
|
||||
|
||||
# 安装预设
|
||||
specify preset add <preset-name>
|
||||
```
|
||||
|
||||
举例来说,预设可以重构规范模板以要求监管追溯性,将工作流适配为你所用的方法论(如敏捷、看板、瀑布、用户任务驱动或领域驱动设计),在方案中添加强制安全审查关卡,强制要求测试优先的任务排序,或将整个工作流本地化为其他语言。[海盗语演示](https://github.com/mnriem/spec-kit-pirate-speak-preset-demo)充分展示了定制的深度。多个预设可按优先级叠加使用。
|
||||
|
||||
完整命令指南以及解析顺序和优先级叠加说明,请参阅[预设参考文档](https://github.github.io/spec-kit/reference/presets.html)。
|
||||
|
||||
## 📦 捆绑包:面向角色的一键配置
|
||||
|
||||
扩展和预设是独立的构建模块。而**捆绑包(bundle)**将一组精选的扩展、预设、步骤和工作流打包成一个带版本、面向角色的配置,从而可以用一条命令为整个团队角色(产品经理、业务分析师、安全研究员、开发者……)完成配置。
|
||||
|
||||
捆绑包由一份手写的 `bundle.yml` 清单描述。它将每个组件锁定到具体版本,并可选择性地面向特定集成;未指定 `integration` 的捆绑包是**中立的**,会沿用项目当前已使用的集成。
|
||||
|
||||
```bash
|
||||
# 在当前激活的目录栈中发现捆绑包
|
||||
specify bundle search [<query>]
|
||||
|
||||
# 查看捆绑包将添加的确切组件集合(与实际安装的内容一致)
|
||||
specify bundle info <bundle-id>
|
||||
|
||||
# 一步安装捆绑包的完整组件集合
|
||||
specify bundle install <bundle-id>
|
||||
|
||||
# 查看已安装内容,然后以非破坏性方式更新或移除
|
||||
specify bundle list
|
||||
specify bundle update <bundle-id> # 或 --all
|
||||
specify bundle remove <bundle-id> # 仅移除此捆绑包的组件
|
||||
```
|
||||
|
||||
捆绑包从一个**按优先级排序的目录栈**(项目 > 用户 > 内置)中解析。每个来源都带有安装策略:`install-allowed` 来源可用于安装,而 `discovery-only` 来源在 `search`/`info` 中可见但拒绝安装。可通过 `specify bundle catalog list|add|remove` 管理目录栈。
|
||||
|
||||
作者在本地校验并打包捆绑包。分发方式是托管构建产物并添加一个目录来源;社区捆绑包投稿请使用 [Bundle Submission](https://github.com/github/spec-kit/issues/new?template=bundle_submission.yml) issue 模板,以便对所需的组件目录和安装证据进行审阅:
|
||||
|
||||
```bash
|
||||
specify bundle validate --path ./my-bundle # 结构与引用检查
|
||||
specify bundle build --path ./my-bundle # 生成带版本的 .zip 产物
|
||||
```
|
||||
|
||||
[`examples/bundles/`](examples/bundles/) 目录下有四份可直接阅读的示例清单(产品经理、业务分析师、安全研究员、开发者)。
|
||||
|
||||
关键保证:`info` 展示的内容与 `install` 添加的内容完全一致(透明性);安装是幂等的,且限定在项目根目录内;`remove` 绝不会触碰其他已安装捆绑包仍需要的组件;所有消费/创作命令都能针对本地或锁定的来源**离线**工作。
|
||||
|
||||
### 何时用哪个
|
||||
|
||||
| 目标 | 使用 |
|
||||
| --- | --- |
|
||||
| 添加全新的命令或工作流 | 扩展 |
|
||||
| 定制规范、方案或任务的格式 | 预设 |
|
||||
| 集成外部工具或服务 | 扩展 |
|
||||
| 强制执行组织或监管规范 | 预设 |
|
||||
| 交付可复用的领域特定模板 | 均可 —— 预设用于模板覆盖,扩展用于随新命令一起打包的模板 |
|
||||
| 用一条命令完成完整的角色配置 | 捆绑包 |
|
||||
|
||||
## 📚 核心理念
|
||||
|
||||
规范驱动开发是一套结构化流程,它强调:
|
||||
|
||||
- **意图驱动开发** —— 让规范先定义"*做什么*",再谈"*怎么做*"
|
||||
- **丰富的规范撰写** —— 借助护栏与组织准则来编写规范
|
||||
- **多步精炼** —— 而非从提示词一次性生成代码
|
||||
- **充分依赖**先进 AI 模型对规范的解读能力
|
||||
|
||||
## 🌟 开发阶段
|
||||
|
||||
| 阶段 | 侧重点 | 关键活动 |
|
||||
| ----------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **从 0 到 1 开发**("绿地/Greenfield") | 从零生成 | <ul><li>从高层需求出发</li><li>生成规范</li><li>规划实现步骤</li><li>构建生产就绪的应用</li></ul> |
|
||||
| **创意探索** | 并行实现 | <ul><li>探索多样化的解决方案</li><li>支持多种技术栈与架构</li><li>试验不同的用户体验模式</li></ul> |
|
||||
| **迭代增强**("棕地/Brownfield") | 存量系统现代化 | <ul><li>迭代式添加功能</li><li>现代化改造遗留系统</li><li>调整流程</li></ul> |
|
||||
|
||||
对于已有项目,请将 Spec Kit 工具本身的更新与功能制品的演进分开处理:升级时刷新受管理的项目文件,而在预期行为发生变化时更新 `specs/` 制品。[规范演进指南](./docs/guides/evolving-specs.md)介绍了推荐的棕地迭代循环。
|
||||
|
||||
## 🎯 实验目标
|
||||
|
||||
我们的研究与实验聚焦于:
|
||||
|
||||
### 技术无关性
|
||||
|
||||
- 使用多样化的技术栈构建应用
|
||||
- 验证这一假设:规范驱动开发是一套流程,不与特定技术、编程语言或框架绑定
|
||||
|
||||
### 企业级约束
|
||||
|
||||
- 展示关键业务应用的开发
|
||||
- 纳入组织层面的约束(云服务商、技术栈、工程实践)
|
||||
- 支持企业设计系统与合规要求
|
||||
|
||||
### 以用户为中心的开发
|
||||
|
||||
- 为不同的用户群体和偏好构建应用
|
||||
- 支持多种开发方式(从"氛围编码"到 AI 原生开发)
|
||||
|
||||
### 创意与迭代流程
|
||||
|
||||
- 验证并行实现探索的理念
|
||||
- 提供稳健的迭代式功能开发工作流
|
||||
- 将流程扩展到升级与现代化改造任务
|
||||
|
||||
## 🔧 环境要求
|
||||
|
||||
- **Linux/macOS/Windows**
|
||||
- [受支持的](#-支持的-ai-编码助手集成) AI 编码助手。
|
||||
- [uv](https://docs.astral.sh/uv/) 用于包管理(推荐),或 [pipx](https://pipx.pypa.io/) 用于持久化安装
|
||||
- [Python 3.11+](https://www.python.org/downloads/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
如果你在使用某个助手时遇到问题,欢迎提交 issue,以便我们完善相应集成。
|
||||
|
||||
## 📖 深入了解
|
||||
|
||||
- **[完整的规范驱动开发方法论](./spec-driven.md)** —— 深入了解整个流程
|
||||
- **[快速上手指南](https://github.github.io/spec-kit/quickstart.html)** —— 分步实现演练
|
||||
|
||||
---
|
||||
|
||||
## 💬 支持
|
||||
|
||||
如需帮助,请提交 [GitHub issue](https://github.com/github/spec-kit/issues/new)。我们欢迎缺陷报告、功能建议,以及关于使用规范驱动开发的各类问题。
|
||||
|
||||
## 🙏 致谢
|
||||
|
||||
本项目深受 [John Lam](https://github.com/jflam) 的工作与研究的影响,并在其基础上构建。
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目基于 MIT 开源许可证的条款授权。完整条款请参阅 [LICENSE](./LICENSE) 文件。
|
||||
@@ -1,57 +1,6 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-08-14T00:00:00Z",
|
||||
"updated_at": "2026-07-15T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/bundles/catalog.community.json",
|
||||
"bundles": {
|
||||
"sicario-spec": {
|
||||
"name": "SicarioSpec Security & Governance Bundle",
|
||||
"id": "sicario-spec",
|
||||
"version": "0.5.1",
|
||||
"role": "security-engineer",
|
||||
"description": "Secure-by-default governance bundle for GitHub Spec Kit. Enforces data classification, threat modeling, and code-owned verification gates.",
|
||||
"author": "SicarioSpec Contributors",
|
||||
"license": "MIT",
|
||||
"download_url": "https://github.com/dfirs1car1o/sicario-spec/releases/download/v0.5.1/sicario-spec-0.5.1.zip",
|
||||
"repository": "https://github.com/dfirs1car1o/sicario-spec",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.9.0"
|
||||
},
|
||||
"provides": {
|
||||
"extensions": 1,
|
||||
"presets": 11,
|
||||
"steps": 0,
|
||||
"workflows": 0
|
||||
},
|
||||
"tags": [
|
||||
"security",
|
||||
"governance",
|
||||
"compliance",
|
||||
"appsec",
|
||||
"threat-modeling"
|
||||
],
|
||||
"verified": false
|
||||
},
|
||||
"specassay": {
|
||||
"name": "SpecAssay",
|
||||
"id": "specassay",
|
||||
"version": "0.3.4",
|
||||
"role": "developer",
|
||||
"description": "Durable-ID promotion for stock Spec Kit: templates, Gate 2 refusal, and trace-manifest emission.",
|
||||
"author": "Rik Dryfoos",
|
||||
"license": "MIT",
|
||||
"download_url": "https://github.com/rdryfoos/specassay/releases/download/v0.3.4/specassay-0.3.4.zip",
|
||||
"repository": "https://github.com/rdryfoos/specassay",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.14.0"
|
||||
},
|
||||
"provides": {
|
||||
"extensions": 1,
|
||||
"presets": 1,
|
||||
"steps": 0,
|
||||
"workflows": 0
|
||||
},
|
||||
"tags": ["traceability", "governance", "durable-ids", "gate", "sdd"],
|
||||
"verified": false
|
||||
}
|
||||
}
|
||||
"bundles": {}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ Accepted community bundle entries are published in [`bundles/catalog.community.j
|
||||
|
||||
| Bundle | Purpose | Role or team | Provides | Required catalogs | URL |
|
||||
|--------|---------|--------------|----------|-------------------|-----|
|
||||
| SicarioSpec Security & Governance Bundle | Secure-by-default governance bundle for GitHub Spec Kit. Enforces data classification, threat modeling, and code-owned verification gates. | `security-engineer` | 1 extension, 11 presets | Documented | [sicario-spec](https://github.com/dfirs1car1o/sicario-spec) |
|
||||
| SpecAssay | Durable-ID promotion for stock Spec Kit: templates, Gate 2 refusal, and trace-manifest emission. | `developer` | 1 extension, 1 preset | Documented | [specassay](https://github.com/rdryfoos/specassay) |
|
||||
|
||||
## What to Submit
|
||||
|
||||
|
||||
@@ -25,21 +25,17 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
|
||||
| Extension | Purpose | Category | Effect | URL |
|
||||
|-----------|---------|----------|--------|-----|
|
||||
| adrkit — decision memory for spec-driven development | Pulls the decisions governing this work into agent context, checks produced plans against them, and drafts an ADR from a plan artifact | `process` | Read+Write | [adrkit](https://github.com/mbeacom/adrkit) |
|
||||
| Agent Assign | Assign specialized Claude Code agents to spec-kit tasks for targeted execution | `process` | Read+Write | [spec-kit-agent-assign](https://github.com/xymelon/spec-kit-agent-assign) |
|
||||
| Agent Governance | Generate agent-platform repository governance files from Spec Kit metadata | `process` | Read+Write | [spec-kit-agent-governance](https://github.com/bigsmartben/spec-kit-agent-governance) |
|
||||
| AI-Driven Engineering (AIDE) | A structured 7-step workflow for building new projects from scratch with AI assistants — from vision through implementation | `process` | Read+Write | [aide](https://github.com/mnriem/spec-kit-extensions/tree/main/aide) |
|
||||
| Analytics | Measure what your AI builds, and how much time it saves you | `visibility` | Read+Write | [spec-kit-analytics](https://github.com/Fyloss/spec-kit-analytics) |
|
||||
| API Evolve | Managed API contract evolution — breaking-change detection, semver enforcement, deprecation orchestration, and lifecycle gates across REST, GraphQL, and gRPC | `process` | Read+Write | [spec-kit-api-evolve](https://github.com/Quratulain-bilal/spec-kit-api-evolve) |
|
||||
| Architect Impact Previewer | Predicts architectural impact, complexity, and risks of proposed changes before implementation. | `visibility` | Read-only | [spec-kit-architect-preview](https://github.com/UmmeHabiba1312/spec-kit-architect-preview) |
|
||||
| Architecture Governance | Keep specs, code & ADRs in sync: citation slots + a read-only, fail-closed validator | `docs` | Read+Write | [spec-kit-arch-governance](https://github.com/ashbrener/spec-kit-arch-governance) |
|
||||
| Architecture Guard | Framework-agnostic architecture review extension for validating implementation against governance and architecture constitutions, detecting architectural drift, and generating non-blocking refactor tasks | `process` | Read+Write | [spec-kit-architecture-guard](https://github.com/DyanGalih/spec-kit-architecture-guard) |
|
||||
| Architecture Workflow | Generate or reverse project-level 4+1 architecture views with per-view and full-workflow commands | `docs` | Read+Write | [spec-kit-arch](https://github.com/bigsmartben/spec-kit-arch) |
|
||||
| Archive Extension | Archive merged features into main project memory, resolving gaps and conflicts. | `docs` | Read+Write | [spec-kit-archive](https://github.com/stn1slv/spec-kit-archive) |
|
||||
| spec-kit-atlas | Synthesize spec-kit specs into faithful, interactive architecture storybooks & doc portals. | `docs` | Read-only | [spec-kit-atlas](https://github.com/ashbrener/spec-kit-atlas) |
|
||||
| Archive Extension | Archive merged features into main project memory. | `docs` | Read+Write | [spec-kit-archive](https://github.com/stn1slv/spec-kit-archive) |
|
||||
| Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | `integration` | Read+Write | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) |
|
||||
| Blueprint | Stay code-literate in AI-driven development: review a complete code blueprint for every task from spec artifacts before /speckit.implement runs | `docs` | Read+Write | [spec-kit-blueprint](https://github.com/chordpli/spec-kit-blueprint) |
|
||||
| Blueprint Index — Living Architecture Map | A living architecture map for spec-driven projects, kept honest by a deterministic, low-friction, machine-first CI gate (JSON, self-healable) that blocks only when the map contradicts the specs or code. Brownfield or greenfield. | `process` | Read+Write | [spec-kit-blueprint](https://github.com/ogil109/spec-kit-blueprint) |
|
||||
| Branch Convention | Configurable branch and folder naming conventions for /specify with presets and custom patterns | `process` | Read+Write | [spec-kit-branch-convention](https://github.com/Quratulain-bilal/spec-kit-branch-convention) |
|
||||
| Brownfield Bootstrap | Bootstrap spec-kit for existing codebases — auto-discover architecture and adopt SDD incrementally | `process` | Read+Write | [spec-kit-brownfield](https://github.com/Quratulain-bilal/spec-kit-brownfield) |
|
||||
| BrownKit | Evidence-driven capability discovery, security and QA risk assessment for existing codebases | `process` | Read+Write | [BrownKit](https://github.com/MaksimShevtsov/BrownKit) |
|
||||
@@ -53,7 +49,6 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
| Coding Standards Drift Control | Generate coding-standards drift reports and remediation tasks for active Spec Kit features | `code` | Read+Write | [spec-kit-coding-standards-drift-control](https://github.com/benizzio/spec-kit-coding-standards-drift-control) |
|
||||
| Conduct Extension | Orchestrates spec-kit phases via sub-agent delegation to reduce context pollution. | `process` | Read+Write | [spec-kit-conduct-ext](https://github.com/twbrandon7/spec-kit-conduct-ext) |
|
||||
| Confluence Extension | Create a doc in Confluence summarizing the specifications and planning files | `integration` | Read+Write | [spec-kit-confluence](https://github.com/aaronrsun/spec-kit-confluence) |
|
||||
| ContextForge MCP | Integrates codebase-memory-mcp + headroom into Spec Kit — graph-based code intelligence and context compression for the implement phase | `code` | Read+Write | [contextforge-mcp](https://github.com/capatinore/contextforge-mcp) |
|
||||
| Cost Tracker | Track real LLM dollar cost across SDD workflows — per-feature budgets, per-integration comparison, and finance-ready exports | `visibility` | Read+Write | [spec-kit-cost](https://github.com/Quratulain-bilal/spec-kit-cost) |
|
||||
| Data Model Diagram | Generates Mermaid ER diagrams from Spec Kit data models after planning | `docs` | Read+Write | [spec-kit-data-model-diagram](https://github.com/benizzio/spec-kit-data-model-diagram) |
|
||||
| DocGuard — CDD Enforcement | The only doc-integrity engine with an MCP server, SARIF/JUnit output, and a deterministic zero-LLM core. Validates, scores, and traces documentation against code — 27 validators, stable finding codes, adoption baseline for legacy repos, compliance-evidence reports, GitHub Action with PR annotations, spec-kit hooks. Pure Node.js, one pinned dep. | `docs` | Read+Write | [spec-kit-docguard](https://github.com/raccioly/docguard) |
|
||||
@@ -70,14 +65,11 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
| Improve Extension | Audits any codebase as a senior advisor and writes prioritized, self-contained spec prompts under specs/ that the spec-kit lifecycle can process | `process` | Read+Write | [spec-kit-improve](https://github.com/d0whc3r/spec-kit-improve) |
|
||||
| Intake | Normalize PRD, design, HTML SSOT, and test-case evidence into SDD-ready intake artifacts. | `docs` | Read+Write | [spec-kit-intake](https://github.com/bigsmartben/spec-kit-intake) |
|
||||
| Intelligent Agent Orchestrator | Cross-catalog agent discovery and intelligent prompt-to-command routing | `process` | Read+Write | [spec-kit-orchestrator](https://github.com/pragya247/spec-kit-orchestrator) |
|
||||
| Intent Reconciliation | Reconcile implementation-discovered decisions against approved feature intent | `process` | Read+Write | [spec-kit-reconcile](https://github.com/SuhaibAslam/spec-kit-reconcile) |
|
||||
| Iterate | Iterate on spec documents with a two-phase define-and-apply workflow — refine specs mid-implementation and go straight back to building | `docs` | Read+Write | [spec-kit-iterate](https://github.com/imviancagrace/spec-kit-iterate) |
|
||||
| Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | `integration` | Read+Write | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) |
|
||||
| Jira Integration (Sync Engine) | Idempotent, drift-aware, fail-closed reconcile engine mirroring spec-kit specs into Jira (Epic per repo, Story per spec, Subtask per phase) | `integration` | Read+Write | [spec-kit-jira-sync](https://github.com/ashbrener/spec-kit-jira-sync) |
|
||||
| Keel Discovery | A Spec Kit extension that puts customer evidence upstream of /speckit.specify, and audits what you shipped against it afterwards | `process` | Read+Write | [spec-kit-keel](https://github.com/keeldiscovery/spec-kit-keel) |
|
||||
| Learning Extension | Generate educational guides from implementations and enhance clarifications with mentoring context | `docs` | Read+Write | [spec-kit-learn](https://github.com/imviancagrace/spec-kit-learn) |
|
||||
| Linear Integration | Mirror spec-kit feature directories into Linear (filesystem → Linear, reconcile-based, unidirectional). | `integration` | Read+Write | [spec-kit-linear-sync](https://github.com/ashbrener/spec-kit-linear-sync) |
|
||||
| Linear Weave | Weave Spec Kit into Linear: pull requirements, mirror tasks.md into sub-issues, sync statuses | `integration` | Read+Write | [spec-kit-linear-weave](https://github.com/tonydwoodhouse/spec-kit-linear-weave) |
|
||||
| LLM Wiki | LLM-maintained compounding project wiki: source ingestion, cited answers, and consistency linting | `docs` | Read+Write | [spec-kit-wiki](https://github.com/formin/spec-kit-wiki) |
|
||||
| Loop Engineering | Engineer safe autonomous agent loops for spec-driven development: a maker/checker split, externalized loop state, and stay-the-engineer guardrails against comprehension debt and cognitive surrender | `process` | Read+Write | [spec-kit-loop](https://github.com/formin/spec-kit-loop) |
|
||||
| MAQA — Multi-Agent & Quality Assurance | Coordinator → feature → QA agent workflow with parallel worktree-based implementation. Language-agnostic. Auto-detects installed board plugins. Optional CI gate. | `process` | Read+Write | [spec-kit-maqa-ext](https://github.com/GenieRobot/spec-kit-maqa-ext) |
|
||||
@@ -147,10 +139,7 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
| Spec Validate | Comprehension validation, review gating, and approval state for spec-kit artifacts — staged quizzes, peer review SLA, and a hard gate before /speckit.implement | `process` | Read+Write | [spec-kit-spec-validate](https://github.com/aeltayeb/spec-kit-spec-validate) |
|
||||
| Spec-Kit BDD | ATDD/BDD extension: convert specs to Gherkin scenarios, scaffold step definitions, and verify acceptance test coverage | `process` | Read+Write | [spec-kit-bdd](https://github.com/RSginer/spec-kit-bdd) |
|
||||
| Spec2Cloud | Spec-driven workflow tuned for shipping to Azure | `process` | Read+Write | [spec2cloud](https://github.com/Azure-Samples/Spec2Cloud) |
|
||||
| SpecAssay Check | Gate 2 refuses silent gaps and emits a trace-manifest (trace-manifest.json). | `visibility` | Read+Write | [specassay](https://github.com/rdryfoos/specassay) |
|
||||
| SpecJudge — right-size the model before you implement | Recommends the model that fits your tasks, citing the spec fragment behind every level. | `process` | Read-only | [SpecJudge](https://github.com/JoaquinRuiz/SpecJudge) |
|
||||
| SpecKit Companion | Live spec-driven progress — lifecycle capture, status, resume, and a turbo pipeline profile | `visibility` | Read+Write | [speckit-companion](https://github.com/alfredoperez/speckit-companion) |
|
||||
| SpecKit Grill Me | Exhaustively resolve specification ambiguities and decisions before planning | `process` | Read+Write | [speckit-grill-me](https://github.com/yoshi1220/speckit-grill-me) |
|
||||
| SpecTest | Auto-generate test scaffolds from spec criteria, map coverage, and find untested requirements | `code` | Read+Write | [spec-kit-spectest](https://github.com/Quratulain-bilal/spec-kit-spectest) |
|
||||
| Squad Bridge | Bootstrap and synchronize a Squad agent team from your Speckit spec and tasks. | `process` | Read+Write | [spec-kit-squad](https://github.com/jwill824/spec-kit-squad) |
|
||||
| Staff Review Extension | Staff-engineer-level code review that validates implementation against spec, checks security, performance, and test coverage | `code` | Read-only | [spec-kit-staff-review](https://github.com/arunt14/spec-kit-staff-review) |
|
||||
@@ -159,7 +148,6 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
| Superpowers Implementation Bridge | Thin orchestrator between Spec Kit (design) and Superpowers (implementation). Cross-agent. | `process` | Read+Write | [speckit-superpowers-bridge](https://github.com/lihan3238/speckit-superpowers-bridge) |
|
||||
| Superspec | Bridges spec-kit with obra/superpowers (brainstorming, TDD, subagent, code-review) into a unified, resumable workflow with graceful degradation and session progress tracking | `process` | Read+Write | [superspec](https://github.com/WangX0111/superspec) |
|
||||
| Tasks to GitHub Project | Publish and synchronize Spec Kit tasks as cards on a GitHub Project (v2) kanban board, with priority and status sync between spec.md/tasks.md and the board. | `integration` | Read+Write | [spec-kit-tasks-to-project](https://github.com/mancioshell/spec-kit-tasks-to-project) |
|
||||
| TDD Extension | Drives spec-kit implementation with tests: a language-agnostic red-green-refactor loop with a per-feature test list, recorded red and green evidence, and mutation-checked test strength. | `process` | Read+Write | [spec-kit-tdd](https://github.com/d0whc3r/spec-kit-tdd) |
|
||||
| Team Assign | Assign tasks.md items to human engineers, split into subtasks, and generate a per-engineer workboard | `process` | Read+Write | [spec-kit-team-assign](https://github.com/tarunkumarbhati/spec-kit-team-assign) |
|
||||
| Test Coverage Drift Control | Generate incremental coverage drift reports and planned remediation tasks after implementation | `code` | Read+Write | [spec-kit-test-coverage-drift-control](https://github.com/benizzio/spec-kit-test-coverage-drift-control) |
|
||||
| Time Machine | Retroactively apply the full SDD workflow to existing codebases — analyse, spec, and ship feature-by-feature | `process` | Read+Write | [spec-kit-time-machine](https://github.com/teeyo/spec-kit-time-machine) |
|
||||
@@ -169,7 +157,7 @@ The following community-contributed extensions are available in [`catalog.commun
|
||||
| Token Economy | Token routing, measured savings, and context audit workflows | `process` | Read+Write | [spec-kit-token-economy](https://github.com/formin/spec-kit-token-economy) |
|
||||
| V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | `docs` | Read+Write | [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 | `code` | Read-only | [spec-kit-verify](https://github.com/ismaelJimenez/spec-kit-verify) |
|
||||
| Verify Review Ship | Post-convergence operational verification, technical review, learning governance, and transactional delivery. | `process` | Read+Write | [spec-kit-verify-review-ship](https://github.com/cadugevaerd/spec-kit-verify-review-ship) |
|
||||
| Verify Review Ship | Adds post-implementation verify, review, and ship readiness gates to Spec Kit workflows | `process` | Read-only | [spec-kit-verify-review-ship](https://github.com/cadugevaerd/spec-kit-verify-review-ship) |
|
||||
| Verify Tasks Extension | Detect phantom completions: tasks marked [X] in tasks.md with no real implementation | `code` | Read-only | [spec-kit-verify-tasks](https://github.com/datastone-inc/spec-kit-verify-tasks) |
|
||||
| Version Guard | Verify tech stack versions against live npm registries before planning and implementation | `process` | Read-only | [spec-kit-version-guard](https://github.com/KevinBrown5280/spec-kit-version-guard) |
|
||||
| What-if Analysis | Preview the downstream impact (complexity, effort, tasks, risks) of requirement changes before committing to them | `visibility` | Read-only | [spec-kit-whatif](https://github.com/DevAbdullah90/spec-kit-whatif) |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Community Friends
|
||||
|
||||
> [!NOTE]
|
||||
> Community projects listed here are independently created and maintained by their respective authors. Unless explicitly marked as a **first-party GitHub project**, they are **not reviewed, nor endorsed, nor supported by GitHub**. Review their source code before installation and use at your own discretion.
|
||||
> Community projects listed here are independently created and maintained by their respective authors. They are **not reviewed, nor endorsed, nor supported by GitHub**. Review their source code before installation and use at your own discretion.
|
||||
|
||||
Community projects that extend, visualize, or build on Spec Kit:
|
||||
|
||||
@@ -16,5 +16,3 @@ Community projects that extend, visualize, or build on Spec Kit:
|
||||
- **[cc-spec-kit](https://github.com/speckit-community/cc-spec-kit)** — Community-maintained plugin for Claude Code and GitHub Copilot CLI that installs Spec Kit skills via the plugin marketplace.
|
||||
|
||||
- **[spectatui](https://github.com/tinesoft/spectatui)** — A terminal UI (TUI) dashboard for Spec Kit that lets you track features, manage specifications, integrations, presets, workflows, and extensions, and monitor AI agent workflows. Attach to existing AI sessions or launch new ones from your terminal. Keyboard and mouse support. Light/dark theme support. Customizable and performance-oriented. Requires the `specify` CLI in your PATH.
|
||||
|
||||
- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — _First-party GitHub project._ A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
|
||||
|
||||
@@ -7,33 +7,27 @@ The following community-contributed presets customize how Spec Kit behaves — o
|
||||
|
||||
| Preset | Purpose | Provides | Requires | URL |
|
||||
|--------|---------|----------|----------|-----|
|
||||
| A11Y Governance | Adds WCAG 2.2 AA, accessible status output, bilingual CEFR-B2 delivery, inclusive-content and didactic-comment governance, and provider-neutral model routing. | 11 templates, 3 commands | — | [spec-kit-preset-a11y-governance](https://github.com/hindermath/spec-kit-preset-a11y-governance) |
|
||||
| Agent Parity Governance | Adds shared-guidance parity, fleet-completion evidence, secret-free runner metadata, audit-ready Spec Kit evidence, and agent-neutral model routing across declared AI-agent surfaces. | 7 templates, 3 commands | — | [spec-kit-preset-agent-parity-governance](https://github.com/hindermath/spec-kit-preset-agent-parity-governance) |
|
||||
| A11Y Governance | Adds accessibility (WCAG 2.2 AA), bilingual DE/EN delivery, CEFR-B2 readability, inclusive-content governance, didactic inline-code-comment review, and audit-ready Spec Kit run evidence | 10 templates, 3 commands | — | [spec-kit-preset-a11y-governance](https://github.com/hindermath/spec-kit-preset-a11y-governance) |
|
||||
| Agent Parity Governance | Adds shared-guidance parity, audit-ready Spec-Kit run evidence, and agent-neutral model-routing guidance across a project's declared AI-agent instruction surfaces so agent guidance does not drift. | 6 templates, 3 commands | — | [spec-kit-preset-agent-parity-governance](https://github.com/hindermath/spec-kit-preset-agent-parity-governance) |
|
||||
| AIDE In-Place Migration | Adapts the AIDE extension workflow for in-place technology migrations (X → Y pattern) — adds migration objectives, verification gates, knowledge documents, and behavioral equivalence criteria | 2 templates, 8 commands | AIDE extension | [spec-kit-presets](https://github.com/mnriem/spec-kit-presets) |
|
||||
| Architecture Governance | Adds secure architecture, STRIDE/CAPEC threat modeling, arc42/S-ADR guidance, Zero Trust, SAMM, BSI cloud assurance, audit evidence, and provider-neutral model routing. | 14 templates, 3 commands | — | [spec-kit-preset-architecture-governance](https://github.com/hindermath/spec-kit-preset-architecture-governance) |
|
||||
| Autonomous Run Governance | Adds permission-bounded autonomous delivery, an optional intake-review gate, and preservation of the project's learner and accessibility contract. | 13 templates, 5 commands, 4 scripts | — | [spec-kit-preset-autonomous-run-governance](https://github.com/hindermath/spec-kit-preset-autonomous-run-governance) |
|
||||
| Architecture Governance | Adds secure software architecture, STRIDE+CAPEC threat modeling, arc42 security cross-cutting concepts, S-ADRs, Zero Trust applicability, OWASP SAMM governance, BSI C3A cloud autonomy, BSI C5 cloud compliance assurance, and audit-ready Spec Kit run evidence | 13 templates, 3 commands | — | [spec-kit-preset-architecture-governance](https://github.com/hindermath/spec-kit-preset-architecture-governance) |
|
||||
| Autonomous Run Governance | Adds permission-bounded, evidence-first governance for autonomous Spec Kit delivery with validated status, stop, resume, exact-head proof, closeout, and learner guidance. | 13 templates, 5 commands, 4 scripts | — | [spec-kit-preset-autonomous-run-governance](https://github.com/hindermath/spec-kit-preset-autonomous-run-governance) |
|
||||
| Canon Core | Adapts original Spec Kit workflow to work together with Canon extension | 2 templates, 8 commands | — | [spec-kit-canon](https://github.com/maximiliamus/spec-kit-canon) |
|
||||
| Claude AskUserQuestion | Upgrades `/speckit.clarify` and `/speckit.checklist` on Claude Code from Markdown-table prompts to the native AskUserQuestion picker, with a recommended option and reasoning on every question | 2 commands | — | [spec-kit-preset-claude-ask-questions](https://github.com/0xrafasec/spec-kit-preset-claude-ask-questions) |
|
||||
| Command Density | Compacts the nine core Spec Kit command prompts while preserving scripts, handoffs, placeholders, hook output blocks, and rule structure | 9 commands | — | [spec-kit-preset-command-density](https://github.com/Xopoko/spec-kit-preset-command-density) |
|
||||
| Cross-Platform Governance | Adds Bash/PowerShell parity, read-only checks, path and native-override review, Unix man pages, bilingual PowerShell help, and provider-neutral model routing. | 9 templates, 3 commands | — | [spec-kit-preset-cross-platform-governance](https://github.com/hindermath/spec-kit-preset-cross-platform-governance) |
|
||||
| Cross-Platform Governance | Adds Bash + PowerShell parity, Unix man-pages, bilingual comment-based help, Verb-Noun Cmdlet discipline, and audit-ready Spec Kit run evidence for scripting projects managed with Spec Kit | 8 templates, 3 commands | — | [spec-kit-preset-cross-platform-governance](https://github.com/hindermath/spec-kit-preset-cross-platform-governance) |
|
||||
| Explicit Task Dependencies | Adds explicit `(depends on T###)` dependency declarations and an Execution Wave DAG to tasks.md for parallel scheduling | 1 template, 1 command | — | [spec-kit-preset-explicit-task-dependencies](https://github.com/Quratulain-bilal/spec-kit-preset-explicit-task-dependencies) |
|
||||
| Fiction Book Writing | It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose principles. Supports interactive elements like brainstorming, interview, roleplay, and extras like statistics, cover builder, illustration builder, and bio command. Export with templates for KDP, D2D, etc. | 26 templates, 34 commands, 2 scripts | — | [speckit-preset-fiction-book-writing](https://github.com/adaumann/speckit-preset-fiction-book-writing) |
|
||||
| Game Narrative Writing | Preset for game narrative design and interactive storytelling. It adapts the Spec-Driven Development workflow for game narratives: features become story mechanics, specs become narrative briefs, plans become story maps, and tasks become dialogue and scene-writing tasks. Supports branching narratives, player agency systems, state machines, and interactive dialogue trees. | 37 templates, 34 commands, 5 scripts | — | [speckit-preset-game-narrative-writing](https://github.com/adaumann/speckit-preset-game-narrative-writing) |
|
||||
| Intake Authoring Governance | Governs traceable intake CRUD, language-aware requirements collections, bounded public HTTPS sources, and explicitly approved single or series authoring. | 13 templates, 5 commands, 7 scripts | — | [spec-kit-preset-intake-authoring-governance](https://github.com/hindermath/spec-kit-preset-intake-authoring-governance) |
|
||||
| Intake Review Governance | Reviews single, series, campaign, and language-aware requirements collections before Spec Kit execution. | 8 templates, 3 commands, 4 scripts | — | [spec-kit-preset-intake-review-governance](https://github.com/hindermath/spec-kit-preset-intake-review-governance) |
|
||||
| Intake Sequencing Governance | Manages language-aware intake-series order, typed dependencies, lifecycle, and authority-neutral next-candidate selection. | 11 templates, 6 commands, 8 scripts | — | [spec-kit-preset-intake-sequencing-governance](https://github.com/hindermath/spec-kit-preset-intake-sequencing-governance) |
|
||||
| iSAQB Architecture Governance | Adds iSAQB/CPSA-F and arc42 architecture governance, architecture views, quality scenarios, ADRs, risks, technical-debt evidence, and provider-neutral model routing. | 14 templates, 3 commands | — | [spec-kit-preset-isaqb-architecture-governance](https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance) |
|
||||
| iSAQB Architecture Governance | Adds general iSAQB/CPSA-F and arc42 software-architecture governance, including audit-ready Spec Kit run evidence for architecture goals, views, quality scenarios, ADRs, risks, and technical debt. | 13 templates, 3 commands | — | [spec-kit-preset-isaqb-architecture-governance](https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance) |
|
||||
| Jira Issue Tracking | Overrides `speckit.taskstoissues` to create Jira epics, stories, and tasks instead of GitHub Issues via Atlassian MCP tools | 1 command | — | [spec-kit-preset-jira](https://github.com/luno/spec-kit-preset-jira) |
|
||||
| Model Driven Engineering | Focuses on streamlined commands, app repository support, cross-spec support, and capability-aware project memory for model-driven engineering workflows | 6 templates, 11 commands | MDE extension | [spec-kit-preset-mde](https://github.com/AI-MDE/spec-kit-preset-mde) |
|
||||
| Model Routing Governance | Maps provider-neutral Spec Kit roles to validated harness-local runner profiles without storing model availability, credentials, or machine-specific selections in Git. | 4 templates, 2 commands, 2 scripts | — | [spec-kit-preset-model-routing-governance](https://github.com/hindermath/spec-kit-preset-model-routing-governance) |
|
||||
| Multi-Repo Branching | Coordinates feature branch creation across multiple git repositories (independent repos and submodules) during plan and tasks phases | 2 commands | — | [spec-kit-preset-multi-repo-branching](https://github.com/sakitA/spec-kit-preset-multi-repo-branching) |
|
||||
| Parallel Autonomous Run Governance | Coordinates permission-bounded autonomous campaigns while preserving the project's learner and accessibility contract across workers and consolidation. | 9 templates, 5 commands, 2 scripts | autonomous-run-governance >=0.2.2; optional: intake-review-governance >=0.1.0 | [spec-kit-preset-parallel-autonomous-run-governance](https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance) |
|
||||
| Pirate Speak (Full) | Transforms all Spec Kit output into pirate speak — specs become "Voyage Manifests", plans become "Battle Plans", tasks become "Crew Assignments" | 6 templates, 9 commands | — | [spec-kit-presets](https://github.com/mnriem/spec-kit-presets) |
|
||||
| Screenwriting | Spec-Driven Development for screenwriting/scriptwriting/tutorials: feature films, television (pilot, episode, limited series), and stage plays. Adapts the Spec Kit workflow to screenplay craft — slug lines, action lines, act breaks, beat sheets, and industry-standard pitch documents. Supports three-act, Save the Cat, TV pilot, network episode, cable/streaming episode, and stage-play structural frameworks. Export to Fountain, FTX, PDF | 26 templates, 32 commands, 1 script | — | [speckit-preset-screenwriting](https://github.com/adaumann/speckit-preset-screenwriting) |
|
||||
| Security Governance | Adds memory-safe-language and secure-coding governance, exact-head security evidence, ASVS, supply-chain transparency, EU regulatory screening, and provider-neutral model routing. | 15 templates, 3 commands | — | [spec-kit-preset-security-governance](https://github.com/hindermath/spec-kit-preset-security-governance) |
|
||||
| Security Governance | Adds memory-safe-language preference, language-specific secure coding profiles, audit-ready Spec-Kit run evidence, ASVS verification, SBOM/AI-SBOM supply-chain transparency, CRA awareness, and regulatory applicability screening for NIS2, CRA, EU AI Act, and DORA | 14 templates, 3 commands | — | [spec-kit-preset-security-governance](https://github.com/hindermath/spec-kit-preset-security-governance) |
|
||||
| SicarioSpec Core | Baseline secure-by-default Spec Kit governance profile. | 5 templates | — | [sicario-spec](https://github.com/dfirs1car1o/sicario-spec) |
|
||||
| Spec2Cloud | Spec-driven workflow tuned for shipping to Azure: spec → plan → tasks → implement → deploy | 5 templates, 8 commands | — | [spec2cloud](https://github.com/Azure-Samples/Spec2Cloud) |
|
||||
| SpecAssay | Appends durable-ID, Carries, and SpecAssay vocabulary onto Spec Kit spec, tasks, and constitution templates. | 3 templates | — | [specassay](https://github.com/rdryfoos/specassay) |
|
||||
| Table of Contents Navigation | Adds a navigable Table of Contents to generated spec.md, plan.md, and tasks.md documents | 3 templates, 3 commands | — | [spec-kit-preset-toc-navigation](https://github.com/Quratulain-bilal/spec-kit-preset-toc-navigation) |
|
||||
| Test-First Governance | Governs TDD with coverage-complete BDD/ATDD Gherkin scenarios, explicit suite ownership, professional test reports, traceability, and risk-based quality gates. | 10 templates, 8 commands | — | [spec-kit-preset-test-first-governance](https://github.com/ka-zo/spec-kit-preset-test-first-governance) |
|
||||
| VS Code Ask Questions | Enhances the clarify command to use `vscode/askQuestions` for batched interactive questioning. | 1 command | — | [spec-kit-presets](https://github.com/fdcastel/spec-kit-presets) |
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
> [!NOTE]
|
||||
> Community walkthroughs are independently created and maintained by their respective authors. They are **not reviewed, nor endorsed, nor supported by GitHub**. Review their content before following along and use at your own discretion.
|
||||
|
||||
See Spec-Driven Development in action across different scenarios with these community-contributed walkthroughs. They are useful read-only examples of completed flows, but they are not official golden outputs for generated specs, plans, or tasks.
|
||||
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.
|
||||
|
||||
|
||||
@@ -63,14 +63,10 @@ independently specified sub-features. Each sub-feature gets its own
|
||||
`spec.md`, `plan.md`, and `tasks.md`, and runs through its own
|
||||
specify/plan/tasks/implement cycle.
|
||||
|
||||
This is the "spec of specs" approach: a first pass breaks a massive feature into
|
||||
smaller, self-contained specs that can each be implemented without overwhelming the
|
||||
model. It adds the most overhead, so reserve it for features that are too large to
|
||||
handle any other way.
|
||||
|
||||
See [Spec of Specs](spec-of-specs.md) for the full procedure — how to run the
|
||||
roadmap pass, structure the roadmap artifact, link sub-specs back to it, and a worked
|
||||
example.
|
||||
This is the "spec of specs" approach: the first iteration breaks a massive
|
||||
feature into smaller, self-contained specs that can each be implemented without
|
||||
overwhelming the model. It adds the most overhead, so reserve it for features
|
||||
that are too large to handle any other way.
|
||||
|
||||
## Which Approach to Choose
|
||||
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
# Spec of Specs
|
||||
|
||||
When a feature is too large to run through a single
|
||||
`/speckit.specify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement`
|
||||
cycle without the model losing track mid-implementation, you can break it into a
|
||||
**roadmap** of smaller, independently-specified sub-features. This is the "spec of
|
||||
specs" approach: one up-front pass decomposes a massive feature into self-contained
|
||||
specs, and each of those runs through its own specify/plan/tasks/implement cycle.
|
||||
|
||||
> **When to reach for this.** Decomposition adds the most overhead of any strategy
|
||||
> in [Handling Complex Features](complex-features.md). Use it **only when the lighter
|
||||
> options there are insufficient** — first try limiting how many tasks run per
|
||||
> `/speckit.implement` invocation, then sub-agent delegation, then a combination.
|
||||
> Reach for a spec of specs only when even a single phase is too large to handle in
|
||||
> one run.
|
||||
|
||||
The rest of this page describes *how* to do it with the tools you already have. No
|
||||
new commands or extensions are required.
|
||||
|
||||
## The roadmap pass
|
||||
|
||||
Before writing any sub-spec, do a single decomposition pass to produce a roadmap.
|
||||
Treat this as a lightweight planning conversation with your agent, not a full spec:
|
||||
|
||||
1. **State the whole feature.** Describe the large feature (the "epic") in a
|
||||
sentence or two so the agent has the full picture up front.
|
||||
2. **Identify independent slices.** Ask the agent to propose a small set of
|
||||
sub-features that each deliver a coherent piece of the epic and can be specified
|
||||
on their own. Aim for slices that are independently testable — implementing just
|
||||
one should leave you with something demonstrable.
|
||||
3. **Draw the boundaries.** For each slice, write one line of intent and an explicit
|
||||
scope boundary (what is in, what is deferred to a sibling slice). Sharp
|
||||
boundaries are what keep each sub-spec small enough to fit in context.
|
||||
4. **Order by dependency.** Note which slices depend on others and sequence them so
|
||||
prerequisites come first. Slices with no dependency on each other can be built in
|
||||
any order. To build independent slices in parallel, use separate worktrees so each
|
||||
run has isolated active-feature state.
|
||||
5. **Record the result as a roadmap.** Capture the slices in a durable roadmap file
|
||||
(below) so every later sub-spec can point back to it.
|
||||
|
||||
The roadmap is deliberately shallow: it names and orders the sub-features but does
|
||||
**not** design them. The design happens when each slice runs through its own
|
||||
`/speckit.specify`.
|
||||
|
||||
## The roadmap artifact
|
||||
|
||||
The roadmap is an ordinary Markdown file you author and keep under version control —
|
||||
there is no special tooling behind it. Put it where the sub-specs can find it:
|
||||
|
||||
- For a feature-scoped epic: `specs/<epic-slug>/roadmap.md`.
|
||||
- For a larger, cross-cutting epic: a top-level `ROADMAP.md`.
|
||||
|
||||
Each roadmap entry carries a stable id (used later for linking), a name, its intent,
|
||||
its scope boundary, its dependencies, a status, and — once the sub-spec exists — a
|
||||
link to it. A minimal template:
|
||||
|
||||
```markdown
|
||||
# Roadmap: <epic name>
|
||||
|
||||
<One or two sentences: what the epic is and why it is being decomposed.>
|
||||
|
||||
**Status legend**: planned · in-progress · done
|
||||
|
||||
| ID | Sub-feature | Intent | Scope boundary | Depends on | Status | Sub-spec |
|
||||
|----|-------------|--------|----------------|-----------|--------|----------|
|
||||
| R1 | <name> | <one line> | <in / deferred> | — | planned | — |
|
||||
| R2 | <name> | <one line> | <in / deferred> | R1 | planned | — |
|
||||
| R3 | <name> | <one line> | <in / deferred> | R1 | planned | — |
|
||||
```
|
||||
|
||||
Keep the `ID` column immutable once a sub-spec references it — it is the anchor for
|
||||
traceability. Fill in the `Sub-spec` column with the path to each sub-feature's spec
|
||||
directory as you create it, and update `Status` as work progresses.
|
||||
|
||||
## Specifying each sub-feature
|
||||
|
||||
With the roadmap in hand, work through the entries one at a time using the normal
|
||||
Spec Kit flow — nothing new to learn:
|
||||
|
||||
1. Pick the next roadmap entry whose dependencies are already `done` (or have none).
|
||||
2. Run `/speckit.specify` for just that slice, describing only its intent and scope
|
||||
from the roadmap entry. Because the slice is bounded, its spec, plan, and tasks
|
||||
stay well within the context window.
|
||||
3. Run `/speckit.plan`, `/speckit.tasks`, and `/speckit.implement` for that slice as
|
||||
usual.
|
||||
4. Mark the roadmap entry `done` and move to the next one.
|
||||
|
||||
Each slice is a complete, independent Spec Kit feature with its own
|
||||
`spec.md`/`plan.md`/`tasks.md`. The roadmap is what ties them together.
|
||||
|
||||
## Linking sub-specs to the roadmap
|
||||
|
||||
To keep scope and intent from drifting across separate runs, every sub-spec
|
||||
references its roadmap entry, and the roadmap links back — a simple, greppable,
|
||||
bidirectional convention:
|
||||
|
||||
- **Sub-spec → roadmap.** In the sub-feature's `spec.md`, name the parent roadmap
|
||||
and entry id in the `Input` / summary line, for example:
|
||||
|
||||
```markdown
|
||||
**Input**: Parent roadmap: `specs/<epic>/roadmap.md` → entry **R3**. <feature description>
|
||||
```
|
||||
|
||||
- **Roadmap → sub-spec.** In the roadmap table, set the entry's `Sub-spec` column to
|
||||
the sub-feature's directory, e.g. `specs/<epic>-part-3/`.
|
||||
|
||||
Because both directions are plain text, you can trace any sub-spec back to its place
|
||||
in the epic (and find its siblings) with a quick search — no tooling, no metadata
|
||||
schema.
|
||||
|
||||
## Keeping the roadmap and sub-specs in sync
|
||||
|
||||
The roadmap is a living document. As you learn more, keep it and the sub-specs
|
||||
aligned:
|
||||
|
||||
- **Roadmap first, then reconcile.** When scope shifts, update the roadmap entry
|
||||
first, then update any sub-specs it affects. The roadmap is the source of truth for
|
||||
how the epic is divided.
|
||||
- **Respect dependencies and ordering.** If a slice depends on another, build the
|
||||
prerequisite first and cross-reference the dependent sub-spec so the relationship
|
||||
is visible from both sides.
|
||||
- **Recurse when a slice is still too big.** If a sub-feature turns out to be too
|
||||
large to specify in one cycle, give it its own roadmap and decompose it further —
|
||||
the same approach applies one level down. Recursion adds overhead, so only go as
|
||||
deep as the context problem actually requires.
|
||||
|
||||
## Worked example
|
||||
|
||||
Suppose the epic is **"Add a self-service billing portal"** — far too large for a
|
||||
single cycle. The roadmap pass breaks it into three independently-specifiable
|
||||
slices.
|
||||
|
||||
`specs/billing-portal/roadmap.md`:
|
||||
|
||||
```markdown
|
||||
# Roadmap: Self-service billing portal
|
||||
|
||||
Let customers view invoices, manage payment methods, and change plans without
|
||||
contacting support. Too large for one cycle, so it is split into independent slices.
|
||||
|
||||
**Status legend**: planned · in-progress · done
|
||||
|
||||
| ID | Sub-feature | Intent | Scope boundary | Depends on | Status | Sub-spec |
|
||||
|----|--------------------|------------------------------------------|---------------------------------------------|-----------|---------|----------|
|
||||
| R1 | Invoice history | Customers view and download past invoices | Read-only; no payment actions | — | done | specs/billing-invoices/ |
|
||||
| R2 | Payment methods | Add, remove, and set a default card | No plan changes; assumes invoices exist | R1 | in-progress | specs/billing-payment-methods/ |
|
||||
| R3 | Plan changes | Upgrade/downgrade the subscription plan | Uses R2's default payment method | R1, R2 | planned | — |
|
||||
```
|
||||
|
||||
Each slice is then specified on its own. For example, the **R2** sub-feature's
|
||||
`spec.md` opens with a back-reference:
|
||||
|
||||
```markdown
|
||||
# Feature Specification: Billing — payment methods
|
||||
|
||||
**Input**: Parent roadmap: `specs/billing-portal/roadmap.md` → entry **R2**.
|
||||
Let customers add, remove, and set a default payment method in the billing portal.
|
||||
```
|
||||
|
||||
From here a reader can trace **R2** back to the roadmap, see that it depends on
|
||||
**R1** (invoice history, already `done`), and see that **R3** (plan changes) is
|
||||
waiting on it. Building R1, then R2, then R3 keeps every run small while the roadmap
|
||||
preserves the shape of the whole epic.
|
||||
|
||||
## For automation (optional)
|
||||
|
||||
If you would rather automate roadmap capture and consistency checks than maintain
|
||||
the file by hand, the community-maintained
|
||||
[Spec Roadmap extension](https://github.com/srobroek/speckit-roadmap) explores that
|
||||
direction. It is a third-party extension and is not required — the manual convention
|
||||
above is enough on its own.
|
||||
@@ -35,27 +35,6 @@ pipx install specify-cli==0.12.11
|
||||
pip install specify-cli==0.12.11
|
||||
```
|
||||
|
||||
## Install from a custom or private package index
|
||||
|
||||
Some environments (corporate networks, mirrors, proxies, or artifact feeds) require installing `specify-cli` from a package index other than the default public PyPI. Each Python tool exposes a way to point at a different index — configure it before running the install commands above. Substitute your own index URL for the placeholder shown here.
|
||||
|
||||
```bash
|
||||
# uv — via environment variable (applies to the whole command)
|
||||
UV_DEFAULT_INDEX=https://your-index.example.com/pypi/simple/ uv tool install specify-cli
|
||||
|
||||
# uv — via flag
|
||||
uv tool install --default-index https://your-index.example.com/pypi/simple/ specify-cli
|
||||
|
||||
# pipx — pass a pip argument through
|
||||
pipx install specify-cli --index-url https://your-index.example.com/pypi/simple/
|
||||
|
||||
# pip
|
||||
pip install specify-cli --index-url https://your-index.example.com/pypi/simple/
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The same index configuration applies to pinned installs, upgrades (`--force`/`--upgrade`), and one-time usage — set the environment variable or flag on those commands too. If your index requires authentication, follow your tool's documentation and prefer credential environment variables, keyring, or netrc; do not embed secrets in command-line URLs because they can leak through shell history, process listings, or logs. Avoid committing secrets. For fully offline installs, see the [air-gapped installation guide](air-gapped.md).
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
|
||||
@@ -50,7 +50,7 @@ pipx install specify-cli
|
||||
pip install specify-cli
|
||||
```
|
||||
|
||||
To install a specific release, pin the version — for example `uv tool install specify-cli==0.12.11`. See the [PyPI installation guide](install/pypi.md) for details, including how to upgrade and how to [install from a custom or private package index](install/pypi.md#install-from-a-custom-or-private-package-index).
|
||||
To install a specific release, pin the version — for example `uv tool install specify-cli==0.12.11`. See the [PyPI installation guide](install/pypi.md) for details, including how to upgrade.
|
||||
|
||||
### One-time Usage
|
||||
|
||||
@@ -77,9 +77,9 @@ specify init <project_name> --integration pi
|
||||
specify init <project_name> --integration omp
|
||||
```
|
||||
|
||||
### Specify Script Type (Shell, PowerShell, or Python)
|
||||
### Specify Script Type (Shell vs PowerShell)
|
||||
|
||||
Automation scripts are available as Bash (`.sh`), PowerShell (`.ps1`), and Python (`.py`) variants.
|
||||
All automation scripts now have both Bash (`.sh`) and PowerShell (`.ps1`) variants.
|
||||
|
||||
Auto behavior:
|
||||
|
||||
@@ -92,7 +92,6 @@ Force a specific script type:
|
||||
```bash
|
||||
specify init <project_name> --script sh
|
||||
specify init <project_name> --script ps
|
||||
specify init <project_name> --script py
|
||||
```
|
||||
|
||||
### Ignore Agent Tools Check
|
||||
@@ -132,7 +131,6 @@ Scripts are installed into a variant subdirectory matching the chosen script typ
|
||||
|
||||
- `.specify/scripts/bash/` — contains `.sh` scripts (default on Linux/macOS)
|
||||
- `.specify/scripts/powershell/` — contains `.ps1` scripts (default on Windows)
|
||||
- `.specify/scripts/python/` — contains `.py` scripts (chosen with `--script py`; also installs the platform shell fallback)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This guide shows how to iterate on the `specify` CLI locally without publishing a release or committing to `main` first.
|
||||
|
||||
> Scripts are available as Bash (`.sh`), PowerShell (`.ps1`), and Python (`.py`) variants. Interactive `specify init` prompts you to choose one; non-interactive runs default to a shell variant for your OS. Pass `--script sh|ps|py` to select explicitly.
|
||||
> Scripts now have both Bash (`.sh`) and PowerShell (`.ps1`) variants. The CLI auto-selects based on OS unless you pass `--script sh|ps`.
|
||||
|
||||
## 1. Clone and Switch Branches
|
||||
|
||||
@@ -189,7 +189,7 @@ rm -rf .venv dist build *.egg-info
|
||||
| `ModuleNotFoundError: typer` | Run `uv pip install -e .` |
|
||||
| Scripts not executable (Linux) | Re-run init or `chmod +x scripts/*.sh` |
|
||||
| Git commands unavailable | Install the git extension with `specify extension add git` |
|
||||
| Wrong script type downloaded | Pass `--script sh`, `--script ps`, or `--script py` explicitly |
|
||||
| Wrong script type downloaded | Pass `--script sh` or `--script ps` explicitly |
|
||||
| TLS errors on corporate network | Configure your environment's certificate store or proxy. The `--skip-tls` flag is deprecated and has no effect. |
|
||||
|
||||
## 14. Next Steps
|
||||
|
||||
+6
-6
@@ -3,9 +3,10 @@
|
||||
This guide will help you get started with Spec-Driven Development using Spec Kit. Throughout, we illustrate each step with a running example: **Taskify**, a small team productivity platform.
|
||||
|
||||
> [!NOTE]
|
||||
> Automation scripts are provided as Bash (`.sh`), PowerShell (`.ps1`), and Python (`.py`) variants. Interactive `specify init` prompts you to choose one; non-interactive runs default to a shell variant for your OS. Pass `--script sh|ps|py` to select explicitly.
|
||||
> Automation scripts are provided as both Bash (`.sh`) and PowerShell (`.ps1`) variants. The `specify` CLI auto-selects based on your OS unless you pass `--script sh|ps`.
|
||||
|
||||
Commands are shown here in `/speckit.*` form, but the exact invocation depends on your agent. Some skills-based agents use `$speckit-*` (e.g. Codex, ZCode) or `/skill:speckit-*` (e.g. Kimi). Use whichever form your agent exposes — the steps are otherwise identical.
|
||||
> [!NOTE]
|
||||
> Commands are shown here in `/speckit.*` form, but the exact invocation depends on your agent. Some skills-based agents use `$speckit-*` (e.g. Codex, ZCode) or `/skill:speckit-*` (e.g. Kimi). Use whichever form your agent exposes — the steps are otherwise identical.
|
||||
|
||||
## Recommended Process
|
||||
|
||||
@@ -82,7 +83,7 @@ Generates the design artifacts from the spec. This is where implementation detai
|
||||
|
||||
### Step 5: `/speckit.checklist` — validate the spec
|
||||
|
||||
Generates a custom quality checklist — "unit tests for your requirements" — to confirm the spec is complete, clear, and consistent before you break the work down. These custom checklists are reviewer-owned requirements-quality review artifacts: mark an item `[x]` only when the reviewer determines that requirement-quality criterion is satisfied. Checked custom items do not mean implementation work is complete.
|
||||
Generates a quality checklist — "unit tests for your requirements" — to confirm the spec is complete, clear, and consistent before you break the work down.
|
||||
|
||||
```text
|
||||
/speckit.checklist
|
||||
@@ -106,7 +107,7 @@ Reports conflicts, gaps, and ambiguities across `spec.md`, `plan.md`, and `tasks
|
||||
|
||||
### Step 8: `/speckit.implement` — build it
|
||||
|
||||
Executes the tasks in `tasks.md` in dependency order. Before implementation, it reads checklist checkbox state as a gate and asks before proceeding if any checklist items are unchecked; it does not change any checklist files or markers. The built-in `checklists/requirements.md` checklist is maintained by `/speckit.specify` and `/speckit.clarify`, while custom checklists remain reviewer-owned. Run it once to build everything, or scope it to one phase at a time for large features.
|
||||
Executes the tasks in `tasks.md` in dependency order. Run it once to build everything, or scope it to one phase at a time for large features.
|
||||
|
||||
```text
|
||||
/speckit.implement
|
||||
@@ -135,6 +136,5 @@ Checks the codebase against the spec, plan, and tasks. If it finds gaps, it appe
|
||||
|
||||
- See the [Agentic SDD](reference/agentic-sdd.md) reference for full detail on every command
|
||||
- Read the [complete methodology](https://github.com/github/spec-kit/blob/main/spec-driven.md) for in-depth guidance
|
||||
- Compare the [core templates](https://github.com/github/spec-kit/tree/main/templates) with
|
||||
[community walkthroughs](community/walkthroughs.md) to see how Spec-Driven Development is used in real projects
|
||||
- Check out [more examples](https://github.com/github/spec-kit/tree/main/templates) in the repository
|
||||
- Explore the [source code on GitHub](https://github.com/github/spec-kit)
|
||||
|
||||
@@ -23,8 +23,6 @@ Creates or updates the project **constitution** — the guiding principles that
|
||||
|
||||
Creates or updates the feature **specification** from a natural-language description. Focus on the **what** and **why** — the user-facing behavior and goals — not the tech stack, which belongs in `/speckit.plan`.
|
||||
|
||||
This workflow may also maintain `checklists/requirements.md`, the built-in spec-quality checklist that `/speckit.specify` creates and `/speckit.clarify` re-evaluates. That lifecycle is separate from custom checklists generated by `/speckit.checklist`.
|
||||
|
||||
```text
|
||||
/speckit.specify Build an application that helps me organize photos into albums grouped by date, re-orderable by drag-and-drop on the main page, with a tile preview inside each album.
|
||||
```
|
||||
@@ -39,8 +37,6 @@ Asks up to five targeted questions about underspecified areas of the current spe
|
||||
|
||||
Clarifying before planning keeps you from designing on top of ambiguity. If `/speckit.analyze` later surfaces requirement gaps, come back and run `/speckit.clarify` (or `/speckit.specify`) again.
|
||||
|
||||
When `checklists/requirements.md` exists, `/speckit.clarify` may update its evaluated state as part of tightening the spec. This exception applies only to the built-in requirements checklist, not to custom review checklists.
|
||||
|
||||
## `/speckit.plan`
|
||||
|
||||
Runs the planning process to generate design artifacts from the spec. This is where implementation detail belongs — provide your tech stack, architecture, and technical constraints as arguments.
|
||||
@@ -53,8 +49,6 @@ Runs the planning process to generate design artifacts from the spec. This is wh
|
||||
|
||||
Generates a quality checklist for the feature — think of it as **"unit tests for your requirements."** Rather than testing code, it checks whether the spec itself is complete, clear, unambiguous, and consistent (for example: "Are the drag-and-drop rules defined for every column?", "Is behavior specified for a deleted assigned user?").
|
||||
|
||||
Custom checklists generated by this command are reviewer-owned requirements-quality review artifacts. An agent may help evaluate them when explicitly asked, but implementation must not silently self-approve them. In a custom checklist, `[x]` means the reviewer determined the requirements-quality criterion is satisfied; it does not mean implementation work is complete.
|
||||
|
||||
Run it with no arguments for a broad pass, or pass a focus area to target one aspect:
|
||||
|
||||
```text
|
||||
@@ -65,7 +59,7 @@ Run it with no arguments for a broad pass, or pass a focus area to target one as
|
||||
/speckit.checklist Focus on the Kanban board interactions and comment permissions.
|
||||
```
|
||||
|
||||
Review the generated checklist. If it surfaces gaps, loop back to `/speckit.clarify` or `/speckit.specify` to tighten the spec before breaking the work down, then mark each custom checklist item `[x]` only after the requirements-quality criterion has been reviewed and satisfied.
|
||||
Review the generated checklist. If it surfaces gaps, loop back to `/speckit.clarify` or `/speckit.specify` to tighten the spec before breaking the work down.
|
||||
|
||||
## `/speckit.tasks`
|
||||
|
||||
@@ -89,8 +83,6 @@ Run it before implementing, while the artifacts can still be adjusted cheaply. I
|
||||
|
||||
Executes the tasks in `tasks.md`, running each phase in dependency order and respecting parallel markers.
|
||||
|
||||
Before executing tasks, it reads checklist checkbox state as a gate. Checklist markers are read-only for this command: `/speckit.implement` counts checked and unchecked items and asks before proceeding when any are unchecked, but it must not change checklist markers. For custom checklists, checked items mean reviewer approval of requirements quality, not completed implementation work.
|
||||
|
||||
For a small feature, run it once to build everything:
|
||||
|
||||
```text
|
||||
|
||||
@@ -12,7 +12,7 @@ specify init [<project_name>]
|
||||
| ------------------------ | ------------------------------------------------------------------------ |
|
||||
| `--integration <key>` | AI coding agent integration to use (e.g. `copilot`, `claude`, `gemini`). See the [Integrations reference](integrations.md) for all available keys |
|
||||
| `--integration-options` | Options for the integration (e.g. `--integration-options="--commands-dir .myagent/cmds"`) |
|
||||
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
||||
| `--script sh\|ps` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||
| `--here` | Initialize in the current directory instead of creating a new one |
|
||||
| `--force` | Force merge/overwrite when initializing in an existing directory |
|
||||
| `--ignore-agent-tools` | Skip checks for AI coding agent CLI tools |
|
||||
@@ -25,7 +25,7 @@ Creates a new Spec Kit project with the necessary directory structure, templates
|
||||
|
||||
Use `<project_name>` to create a new directory, or `--here` (or `.`) to initialize in the current directory. If the directory already has files, use `--force` to merge without confirmation.
|
||||
|
||||
When `--integration` is omitted, interactive terminals prompt you to choose an integration. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot; pass `--integration <key>` to choose a different integration explicitly, or set `SPECKIT_INTEGRATION_DEFAULT` to change the fallback (see [Environment Variables](#environment-variables)).
|
||||
When `--integration` is omitted, interactive terminals prompt you to choose an integration. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot; pass `--integration <key>` to choose a different integration explicitly.
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -50,15 +50,12 @@ specify init my-project --integration copilot --preset compliance
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------ |
|
||||
| `SPECKIT_INTEGRATION_DEFAULT` | Override the fallback integration used by `specify init` when `--integration` is omitted (interactive prompt default and non-interactive fallback). Set it to any registered integration key (e.g. `gemini`, `claude`). An unrecognized value is ignored with a warning and the built-in default (`copilot`) is used. An explicit `--integration <key>` always takes precedence. |
|
||||
| `SPECIFY_INIT_DIR` | Target a member project from outside its directory (e.g. a monorepo root) without `cd`, for non-interactive / CI use. Set it to the **project root** — the directory *containing* `.specify/` (relative paths resolve against the current directory). The path must exist and contain `.specify/`, otherwise the command errors and does **not** fall back to the current directory. Resolved once in the core root helper (`get_repo_root` in Bash, `Get-RepoRoot` in PowerShell), so it is honored by the core feature scripts (`/speckit.plan`, `/speckit.tasks`, …) and the Git extension's feature-branch creation, which inherit it. The `specify` CLI applies the **same** validation rules to every project-scoped subcommand (`specify integration …`, `specify extension …`, `specify workflow …`, `specify preset …`, and the rest that operate on a `.specify/` project), so those can target a member project too. When unset, Bash/PowerShell helpers keep their existing upward search; the `specify` CLI keeps its project-scoped resolver cwd-only unless a command explicitly defines broader detection (for example, bundle commands). |
|
||||
| `SPECIFY_FEATURE_DIRECTORY` | Override the active feature directory *within* the resolved project (takes precedence over `.specify/feature.json`). Relative paths resolve under the project root. Combine with `SPECIFY_INIT_DIR` to pick both the project and the feature non-interactively. |
|
||||
| `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. Must be set in the context of the agent prior to using `/speckit.plan` or follow-up commands. |
|
||||
|
||||
> **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature.
|
||||
|
||||
> **Version control.** `specify init` scaffolds a managed `.specify/.gitignore` that excludes machine-local state — `feature.json` (the current-feature pointer, rewritten on every feature switch) and per-machine extension `extensions/*/local-config.yml` overrides — while leaving everything else under `.specify/` (constitution, templates, scripts, extension config) shareable so teams stay aligned. Like the rest of `.specify/`'s shared scripts and templates, the file is tracked in the shared-infrastructure manifest: your edits are preserved on re-init and `specify init --here --force` restores the managed content. It is intentionally left in place by `specify integration uninstall`, which only removes the uninstalled agent's own files.
|
||||
|
||||
> **Symlinked project roots.** `SPECIFY_INIT_DIR` relocates *where* the project is, not *how* a command treats symlinks: each command keeps its existing cwd-path stance. Commands that traverse and write project files through broad input paths (`bundle`, `workflow run <file>`) refuse a symlinked `.specify/` to preserve write confinement. Other project-scoped commands keep their existing behavior when `SPECIFY_INIT_DIR` points at a project root, which may include following a symlinked `.specify/`.
|
||||
|
||||
## Check Installed Tools
|
||||
|
||||
@@ -221,14 +221,12 @@ Each hook entry supports the following fields:
|
||||
| `command` | Extension command associated with the hook. |
|
||||
| `enabled` | Whether the hook is active. Hooks with `enabled: false` are skipped. |
|
||||
| `optional` | Whether the hook is optional. If `true`, the hook is presented with its `prompt` and can be skipped; if `false`, the hook is emitted as an automatic hook (includes `EXECUTE_COMMAND` markers). |
|
||||
| `priority` | Priority metadata for the hook. Registered hook entries use integer values >= 1; entries installed from manifests default to `10` when no priority is declared. Current command templates surface hooks in their configured YAML order and do not sort them by `priority`. |
|
||||
| `priority` | Priority metadata for the hook. Values must be integers >= 1; invalid values fall back to the default priority `10`. Current command templates surface hooks in their configured YAML order and do not sort them by `priority`. |
|
||||
| `prompt` | Message shown when asking whether to run an optional hook. |
|
||||
| `description` | Human-readable explanation of what the hook does. |
|
||||
| `condition` | Optional expression evaluated by `HookExecutor` (using `config.<path>` or `env.<VAR>` with `is set`, `==`, or `!=`). Current command templates do not evaluate conditions and skip hooks with a non-empty condition. |
|
||||
Hook event names identify when a hook is invoked. They generally use `before_<command>` or `after_<command>`, such as `before_implement`, `after_implement`, `before_tasks`, and `after_tasks`.
|
||||
|
||||
Extension manifests reject invalid hook priorities during installation. For existing `.specify/extensions.yml` entries, `HookExecutor.get_hooks_for_event()` sorts with `normalize_priority()`: missing values, booleans, non-numeric values rejected by `int()`, and values less than `1` fall back to `10`; numeric strings and finite floats are coerced with `int()`, while non-finite floats are unsupported and may fail instead of falling back.
|
||||
|
||||
`HookExecutor.get_hooks_for_event()` returns hooks ordered by `priority`, with lower values first. However, current command templates read hook lists directly and surface them in their configured YAML order rather than using priority ordering.
|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -6,7 +6,6 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
|
||||
|
||||
| Agent | Key | Notes |
|
||||
| ------------------------------------------------------------------------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Alquimia AI](https://docs.alquimia.ai) | `alquimia` | Skills-based integration; installs skills into `.alquimia/skills` and invokes them as `/speckit-<command>` |
|
||||
| [Amp](https://ampcode.com/) | `amp` | |
|
||||
| [Antigravity (agy)](https://antigravity.google/) | `agy` | Skills-based integration; skills are installed automatically |
|
||||
| [Auggie CLI](https://docs.augmentcode.com/cli/overview) | `auggie` | |
|
||||
@@ -14,20 +13,18 @@ The Specify CLI supports a wide range of AI coding agents. When you run `specify
|
||||
| [Cline](https://github.com/cline/cline) | `cline` | IDE-based agent |
|
||||
| [CodeBuddy CLI](https://www.codebuddy.cn/docs/cli/installation) | `codebuddy` | |
|
||||
| [Codex CLI](https://github.com/openai/codex) | `codex` | Skills-based integration; installs skills into `.agents/skills` and invokes them as `$speckit-<command>` |
|
||||
| [Command Code](https://commandcode.ai/docs) | `command-code` | Skills-based integration; installs skills into `.commandcode/skills/` and invokes them as `$speckit-<command>` |
|
||||
| [Cursor](https://cursor.sh/) | `cursor-agent` | |
|
||||
| [Devin for Terminal](https://cli.devin.ai/docs) | `devin` | Skills-based integration; installs skills into `.devin/skills/` and invokes them as `/speckit-<command>` |
|
||||
| [Factory Droid](https://docs.factory.ai/cli/getting-started/overview) | `droid` | Skills-based integration; installs skills into `.factory/skills/` and invokes them as `/speckit-<command>` |
|
||||
| [Firebender](https://firebender.com/) | `firebender` | IDE-based agent for Android Studio / IntelliJ |
|
||||
| [Forge](https://forgecode.dev/) | `forge` | |
|
||||
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | |
|
||||
| [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Skills-based by default; installs `speckit-<command>/SKILL.md` under `.github/skills/`. Pass `--integration-options="--commands"` to use the supported commands layout: `.agent.md` files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. |
|
||||
| [GitHub Copilot](https://code.visualstudio.com/) | `copilot` | Defaults to legacy markdown mode: `.agent.md` command files under `.github/agents/`, companion `.prompt.md` files under `.github/prompts/`, and a `.vscode/settings.json` merge. Pass `--integration-options="--skills"` to scaffold skills as `speckit-<command>/SKILL.md` under `.github/skills/` instead. Legacy markdown mode is deprecated and will stop being the default in a future release. |
|
||||
| [Goose](https://goose-docs.ai/) | `goose` | Uses YAML recipe format in `.goose/recipes/` |
|
||||
| [Grok Build](https://docs.x.ai/build/overview) | `grok` | Skills-based integration; installs skills into `.grok/skills` and invokes them as `/speckit-<command>` |
|
||||
| [Hermes](https://github.com/NousResearch/hermes-agent) | `hermes` | Skills-based integration; installs skills globally into `~/.hermes/skills/` |
|
||||
| [IBM Bob](https://www.ibm.com/products/bob) | `bob` | Skills-based integration by default; installs skills as `speckit-<command>/SKILL.md` under `.bob/skills/` and invokes them as `/speckit-<command>`. Pass `--integration-options="--legacy-commands"` to scaffold the deprecated Bob 1.x layout (`.bob/commands/*.md`) instead; that flag will be removed in a future release. Existing legacy installs can migrate with `specify integration upgrade bob --integration-options="--skills"`, which converts them to the skills layout and removes the old command files. If preset overrides are installed, the migration is rejected with an actionable error (preset artifacts cannot yet be reconciled across a layout change) — remove the preset(s), migrate, then reinstall them. |
|
||||
| [Junie](https://junie.jetbrains.com/) | `junie` | |
|
||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | `kilocode` | Installs commands into `.kilo/commands`; legacy `.kilocode/workflows` installs remain supported as a registration fallback |
|
||||
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | `kilocode` | |
|
||||
| [Kimi Code](https://code.kimi.com/) | `kimi` | Skills-based integration; installs into `.kimi-code/skills/`. `--migrate-legacy` moves old `.kimi/skills/` installs to the new paths |
|
||||
| [Kiro CLI](https://kiro.dev/docs/cli/) | `kiro-cli` | Kiro CLI does not substitute `$ARGUMENTS` in file-based prompts, so Spec Kit ships a prose fallback at render time (see [Manage prompts](https://kiro.dev/docs/cli/chat/manage-prompts/) and issue [#1926](https://github.com/github/spec-kit/issues/1926)). Alias: `--integration kiro` |
|
||||
| [Lingma](https://lingma.aliyun.com/) | `lingma` | Skills-based integration; skills are installed automatically |
|
||||
@@ -88,7 +85,7 @@ specify integration install <key>
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------ | ------------------------------------------------------------------------ |
|
||||
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
||||
| `--script sh\|ps` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||
| `--force` | Opt in to installing alongside integrations that are not declared multi-install safe |
|
||||
| `--integration-options` | Integration-specific options (e.g. `--integration-options="--commands-dir .myagent/cmds"`) |
|
||||
|
||||
@@ -96,8 +93,6 @@ Installs the specified integration into the current project. If another integrat
|
||||
|
||||
Installing an additional integration does not change the default integration. Use `specify integration use <key>` to change the default.
|
||||
|
||||
Installed extensions and presets are not registered for a non-default integration at install time — they follow the currently active (default) integration only. `specify integration use <key>` (or `switch <key>`) is what rescaffolds them for the newly active integration.
|
||||
|
||||
> **Note:** All integration management commands require a project already initialized with `specify init`. To start a new project with a specific agent, use `specify init <project> --integration <key>` instead.
|
||||
|
||||
**Version note:** Controlled multi-install support was introduced in Spec Kit 0.8.5. If `specify integration install <key>` says another integration is already installed and only suggests `switch` or `uninstall`, check your local CLI with `specify version` and upgrade it. Running a one-shot command such as `uvx --from git+https://github.com/github/spec-kit.git specify ...` uses a temporary copy for that command only; it does not update the persistent `specify` executable on your `PATH`.
|
||||
@@ -126,12 +121,12 @@ specify integration switch <key>
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------ | ------------------------------------------------------------------------ |
|
||||
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
||||
| `--script sh\|ps` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||
| `--force` | Force removal of modified files during uninstall; when the target is already installed, overwrite managed shared templates while changing the default |
|
||||
| `--refresh-shared-infra` | Also overwrite shared infrastructure files even if you customized them (otherwise customizations are preserved) |
|
||||
| `--integration-options` | Options for the target integration when it is not already installed |
|
||||
|
||||
If the target integration is not already installed, equivalent to running `uninstall` followed by `install` in a single step. In this mode, `--force` controls whether modified files from the removed integration are deleted. If the target integration is already installed, `switch` only changes the default integration, like `use`; in this mode, `--force` controls whether managed shared templates are overwritten while the default changes. `--integration-options` is rejected for already-installed targets because changing integration options requires reinstalling managed files; run `upgrade <key> --integration-options ...` first, then `use <key>`. Like `use`, `switch` rescaffolds installed extensions and presets for the target integration once it becomes the default.
|
||||
If the target integration is not already installed, equivalent to running `uninstall` followed by `install` in a single step. In this mode, `--force` controls whether modified files from the removed integration are deleted. If the target integration is already installed, `switch` only changes the default integration, like `use`; in this mode, `--force` controls whether managed shared templates are overwritten while the default changes. `--integration-options` is rejected for already-installed targets because changing integration options requires reinstalling managed files; run `upgrade <key> --integration-options ...` first, then `use <key>`.
|
||||
|
||||
## Use an Installed Integration
|
||||
|
||||
@@ -145,8 +140,6 @@ specify integration use <key>
|
||||
|
||||
Sets the default integration without uninstalling any other installed integrations. This also refreshes managed shared templates so command references match the new default integration's invocation style. Modified or untracked shared templates are preserved unless `--force` is used.
|
||||
|
||||
`use` is also the activation point for installed extensions and presets: it re-registers every enabled extension's and preset's command overrides (and skills, for skills-mode agents) for the newly active integration, so artifacts installed while a different integration was active are rescaffolded here rather than at install time.
|
||||
|
||||
## Upgrade an Integration
|
||||
|
||||
```bash
|
||||
@@ -156,15 +149,11 @@ specify integration upgrade [<key>]
|
||||
| Option | Description |
|
||||
| ------------------------ | ------------------------------------------------------------------------ |
|
||||
| `--force` | Overwrite files even if they have been modified |
|
||||
| `--script sh\|ps\|py` | Script type: `sh` (bash/zsh), `ps` (PowerShell), or `py` (Python) |
|
||||
| `--script sh\|ps` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||
| `--integration-options` | Options for the integration |
|
||||
|
||||
Reinstalls an installed integration with updated templates and commands (e.g., after upgrading Spec Kit). Defaults to the default integration; if a key is provided, it must be one of the installed integrations. Detects locally modified files and blocks the upgrade unless `--force` is used. Stale files from the previous install that are no longer needed are removed automatically. Shared templates stay aligned with the default integration even when upgrading a non-default integration.
|
||||
|
||||
Enabled extensions and presets are re-registered only when upgrading the currently active (default) integration. A non-default upgrade still refreshes that integration's core commands, but does not re-register its extension or preset layers — `use`/`switch` that integration afterward to rescaffold them.
|
||||
|
||||
If an upgrade would change an integration between command and skills layouts while preset artifacts are registered for it, the upgrade is rejected before changing files. Remove the affected presets, run the layout-changing upgrade, then reinstall them.
|
||||
|
||||
## Report Integration Status
|
||||
|
||||
```bash
|
||||
@@ -235,8 +224,7 @@ Some integrations accept additional options via `--integration-options`:
|
||||
| ----------- | ------------------- | -------------------------------------------------------------- |
|
||||
| `generic` | `--commands-dir` | Required. Directory for command files |
|
||||
| `kimi` | `--migrate-legacy` | Migrate legacy `.kimi/skills/` installs to `.kimi-code/skills/` (including dotted→hyphenated skill naming, e.g. `speckit.xxx` → `speckit-xxx`) |
|
||||
| `copilot` | `--commands` | Scaffold `.github/agents/*.agent.md` commands with `.github/prompts/*.prompt.md` companions and merge `.vscode/settings.json` instead of using the default skills layout. |
|
||||
| `copilot` | `--skills` | Force the default skills layout, overriding an existing commands layout during an explicit migration. |
|
||||
| `copilot` | `--skills` | Scaffold commands as agent skills (`speckit-<command>/SKILL.md` under `.github/skills/`, invoked as `/speckit-<command>`) instead of the default legacy markdown mode (`.github/agents/*.agent.md` plus `.github/prompts/*.prompt.md` and a `.vscode/settings.json` merge). Without this flag, install warns that legacy markdown mode is deprecated. |
|
||||
|
||||
Example:
|
||||
|
||||
@@ -268,36 +256,31 @@ Spec Kit tracks one default integration in `.specify/integration.json` with `def
|
||||
|
||||
An integration is multi-install safe when it uses a static, unique agent root and command directory, stable command invocation settings, and a separate install manifest whose managed files do not overlap another safe integration. Registry tests enforce those path and manifest invariants. Shared Spec Kit templates remain aligned to the single default integration.
|
||||
|
||||
The Command directory column below lists the directory each integration installs its commands or skills into. Context-file targeting is a separate concern from integration multi-install safety: `multi_install_safe` is an integration declaration about command/skill paths, whereas the optional agent-context extension manages a per-agent context file (for example `AGENTS.md` or `CLAUDE.md`) and can even synchronize several anchors at once via its `context_files` setting. Multiple agents mapping to the same context file is expected there and does not affect whether an integration is multi-install safe; see the agent-context extension for details.
|
||||
The Isolation column below lists paths Spec Kit manages for that integration (skills/commands roots and any integration-owned rule files). It is not a full inventory of every file an agent may read.
|
||||
|
||||
**Agent-context defaults are separate.** The optional agent-context extension maps each integration to a default context file in `extensions/agent-context/agent-context-defaults.json`. Those defaults are independent of multi-install safety: several agents may share a root file such as `AGENTS.md` when the extension is enabled. Multi-install safety does not require a unique context file per safe integration.
|
||||
|
||||
The currently declared multi-install safe integrations are:
|
||||
|
||||
| Key | Command directory |
|
||||
| --- | ----------------- |
|
||||
| `alquimia` | `.alquimia/skills` |
|
||||
| `auggie` | `.augment/commands` |
|
||||
| `claude` | `.claude/skills` |
|
||||
| `cline` | `.clinerules/workflows` |
|
||||
| `codebuddy` | `.codebuddy/commands` |
|
||||
| `codex` | `.agents/skills` |
|
||||
| `command-code` | `.commandcode/skills` |
|
||||
| `cursor-agent` | `.cursor/skills` |
|
||||
| `droid` | `.factory/skills` |
|
||||
| `firebender` | `.firebender/commands` |
|
||||
| `gemini` | `.gemini/commands` |
|
||||
| Key | Isolation |
|
||||
| --- | --------- |
|
||||
| `auggie` | `.augment/commands`, `.augment/rules/specify-rules.md` |
|
||||
| `claude` | `.claude/skills`, `CLAUDE.md` |
|
||||
| `cline` | `.clinerules/workflows`, `.clinerules/specify-rules.md` |
|
||||
| `codebuddy` | `.codebuddy/commands`, `CODEBUDDY.md` |
|
||||
| `codex` | `.agents/skills`, `AGENTS.md` |
|
||||
| `cursor-agent` | `.cursor/skills`, `.cursor/rules/specify-rules.mdc` |
|
||||
| `firebender` | `.firebender/commands`, `.firebender/rules/specify-rules.mdc` |
|
||||
| `gemini` | `.gemini/commands`, `GEMINI.md` |
|
||||
| `grok` | `.grok/skills` |
|
||||
| `junie` | `.junie/commands` |
|
||||
| `kilocode` | `.kilo/commands` |
|
||||
| `kiro-cli` | `.kiro/prompts` |
|
||||
| `lingma` | `.lingma/skills` |
|
||||
| `omp` | `.omp/commands` |
|
||||
| `pi` | `.pi/prompts` |
|
||||
| `qodercli` | `.qoder/commands` |
|
||||
| `qwen` | `.qwen/commands` |
|
||||
| `shai` | `.shai/commands` |
|
||||
| `tabnine` | `.tabnine/agent/commands` |
|
||||
| `trae` | `.trae/skills` |
|
||||
| `zcode` | `.zcode/skills` |
|
||||
| `junie` | `.junie/commands`, `.junie/AGENTS.md` |
|
||||
| `kilocode` | `.kilocode/workflows`, `.kilocode/rules/specify-rules.md` |
|
||||
| `qodercli` | `.qoder/commands`, `QODER.md` |
|
||||
| `qwen` | `.qwen/commands`, `QWEN.md` |
|
||||
| `shai` | `.shai/commands`, `SHAI.md` |
|
||||
| `tabnine` | `.tabnine/agent/commands`, `TABNINE.md` |
|
||||
| `trae` | `.trae/skills`, `.trae/rules/project_rules.md` |
|
||||
| `zcode` | `.zcode/skills`, `ZCODE.md` |
|
||||
|
||||
Integrations that share a command directory with another integration, require dynamic install paths such as `--commands-dir`, or merge shared tool settings are not declared safe by default. They can still be installed alongside another integration with `--force`.
|
||||
|
||||
@@ -316,7 +299,3 @@ CLI-based integrations (like Claude Code, Gemini CLI) require the tool to be ins
|
||||
### When should I use `upgrade` vs `switch`?
|
||||
|
||||
Use `upgrade` when you've upgraded Spec Kit and want to refresh an installed integration's managed files. Use `switch` when you want to replace the current default with another integration; if the target is already installed, `switch` behaves like `use`.
|
||||
|
||||
### Do extensions and presets I install apply to every installed integration?
|
||||
|
||||
No. Extensions (`specify extension add`) and presets (`specify preset add`) register their command overrides for the currently active (default) integration only, even if other integrations are installed. A non-default integration does not receive those artifacts until it becomes the default: `specify integration use <key>` (or `switch <key>`) rescaffolds every enabled extension and preset for the newly active integration. `specify integration upgrade` follows the same rule — it only re-registers extensions and presets when upgrading the active integration.
|
||||
|
||||
@@ -47,8 +47,6 @@ specify preset list
|
||||
|
||||
Lists installed presets with their versions, descriptions, template counts, and current status.
|
||||
|
||||
Presets are printed in **resolution/precedence order**: the highest-precedence preset (lowest priority number) is listed first, and ties on priority are broken alphabetically by preset id. This matches the order used when composing commands and resolving templates, so the top entry is the one that wins for overlapping files.
|
||||
|
||||
## Preset Info
|
||||
|
||||
```bash
|
||||
@@ -141,7 +139,7 @@ catalogs:
|
||||
|
||||
Presets can provide command files, template files (like `plan-template.md`), and script files. Each file name is evaluated independently against the priority stack, so different files can come from different layers.
|
||||
|
||||
Templates and scripts are looked up from the stack when Spec Kit needs them. Commands use the same stack for replacement and composition, but are materialized into the active integration's directory only, instead of being re-resolved by agents or written to every detected agent directory (#2948). During preset install, Spec Kit registers command files for the preset being installed against the currently active integration; post-install and post-removal reconciliation then recomputes and writes the effective command content for affected command names based on the active stack. Install and rescaffold remain active-only, but removal may also update previously targeted inactive directories recorded by the removed preset to restore the surviving command or skill layer. A non-active installed integration does not otherwise receive these command files until it becomes the default — `specify integration use <key>` (or `switch <key>`) rescaffolds enabled presets for the newly active integration. Agents do not re-resolve the stack each time they run a command.
|
||||
Templates and scripts are looked up from the stack when Spec Kit needs them. Commands use the same stack for replacement and composition, but are materialized into detected agent directories instead of being re-resolved by agents. During preset install, Spec Kit registers command files for the preset being installed; post-install and post-removal reconciliation then recomputes and writes the effective command content for affected command names based on the active stack. Agents do not re-resolve the stack each time they run a command.
|
||||
|
||||
By default, files use a **replace** strategy: the first match in the priority stack wins and is used entirely. Templates and commands can also use composition strategies: **prepend** places preset content before lower-priority content, **append** places it after lower-priority content, and **wrap** replaces `{CORE_TEMPLATE}` with lower-priority content. Scripts support **replace** and **wrap**; script wrappers use `$CORE_SCRIPT` as the placeholder.
|
||||
|
||||
|
||||
+3
-111
@@ -39,21 +39,6 @@ specify workflow run my-pipeline.yml --json
|
||||
|
||||
`workflow_id` is the `workflow.id` declared inside the YAML, not the file name. The object is printed exactly as shown — pretty-printed with two-space indentation, on plain stdout with no Rich markup — so it always parses. While the workflow runs under `--json`, any progress a step would print (for example a gate prompt, or output from a prompt step's CLI subprocess) is redirected to stderr, so stdout carries only the JSON object. Read the object from stdout; leave stderr attached to the terminal or capture it separately.
|
||||
|
||||
For `failed` and `aborted` runs, the payload includes an `error` field carrying the terminal step's error message:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "662bf791",
|
||||
"workflow_id": "build-and-review",
|
||||
"status": "failed",
|
||||
"current_step_id": "boom",
|
||||
"current_step_index": 0,
|
||||
"error": "Command exited with code 3"
|
||||
}
|
||||
```
|
||||
|
||||
`completed` and `paused` runs omit the `error` field. The error is persisted in the run's `state.json`, so `specify workflow status <run_id> --json` surfaces the same message after the fact.
|
||||
|
||||
> **Note:** Most workflow commands require a project already initialized with `specify init`. The exception is `specify workflow run <local-file.{yml,yaml}>`, which can run outside a project; in that case, run state is stored under the current directory's `.specify/workflows/runs/<run_id>/`.
|
||||
|
||||
## Resume a Workflow
|
||||
@@ -103,17 +88,10 @@ specify workflow add <source>
|
||||
|
||||
| Option | Description |
|
||||
| --------------- | ------------------------------------------------------ |
|
||||
| `--dev` | Install from a local YAML file, package directory, or archive |
|
||||
| `--dev` | Install from a local workflow YAML file or directory |
|
||||
| `--from <url>` | Install from a custom URL (`<source>` names the expected workflow ID) |
|
||||
|
||||
Installs a workflow from the catalog, an HTTPS URL, a local YAML file, a
|
||||
directory containing `workflow.yml`, or a `.zip`, `.tar.gz`, or `.tgz`
|
||||
archive. Archives may contain `workflow.yml` at the root or inside one
|
||||
top-level directory.
|
||||
|
||||
Directory and archive installs preserve the complete workflow package,
|
||||
including scripts and other companion files. ZIP, `.tar.gz`, and `.tgz`
|
||||
archives follow the same validation and installation behavior.
|
||||
Installs a workflow from the catalog, a URL (HTTPS required), a local YAML file, or a local directory containing `workflow.yml`.
|
||||
|
||||
## Workflow Overlays
|
||||
|
||||
@@ -288,9 +266,7 @@ Lower priority values have higher precedence. Change this overlay to `priority:
|
||||
|
||||
### Interaction with Bundles and Updates
|
||||
|
||||
`specify workflow add <local-directory>` installs the complete local workflow
|
||||
package into `.specify/workflows/<id>/`. Archive installs preserve the same
|
||||
package contents.
|
||||
`specify workflow add <local-directory>` installs `workflow.yml` from the local directory into `.specify/workflows/<id>/`.
|
||||
|
||||
When an installed workflow is refreshed or reinstalled, project overlays in `.specify/workflows/overlays/<id>/` are preserved because they live outside the installed workflow directory.
|
||||
|
||||
@@ -526,32 +502,6 @@ args: "{{ inputs.spec }}"
|
||||
message: "{{ status | default('pending') }}"
|
||||
```
|
||||
|
||||
### Interpolation and shell safety
|
||||
|
||||
Expressions are resolved by **plain string substitution** — the value of `{{ ... }}` is spliced into the surrounding text exactly as-is, with no quoting or escaping added. That is convenient for building `args` and `message` strings, but it has an important consequence for `shell` steps: a `run` field is handed to the system shell (`/bin/sh -c` on POSIX), so any interpolated value is interpreted as **shell syntax**, not just data.
|
||||
|
||||
If an interpolated value can contain characters like `;`, `|`, `&`, `$( )`, backticks, or quotes, it can change or extend the command that actually runs. This matters most when the value is not fully under the workflow author's control:
|
||||
|
||||
- **Workflow `inputs.*`** — supplied by whoever runs the workflow.
|
||||
- **A prior step's output**, e.g. `{{ steps.plan.output.stdout }}` — for a `prompt` step this is **text produced by the AI agent**, which can in turn be influenced by files, tickets, or web content the agent read. Treat agent output as untrusted when it flows into a `shell` step.
|
||||
|
||||
There is **no shell-escaping filter** in the expression language and **no sandbox** around a `shell` step, so none of the practices below can be treated as a guarantee that a hostile value is neutralised. The only reliable control is to constrain what an interpolated value *can* be, and to keep values you cannot constrain out of `run` fields entirely. Scrutinise every `run` field that interpolates a value you do not control, and at minimum:
|
||||
|
||||
- **Constrain the value at the source with `enum`/an allowlist.** When `inputs.*` feeds a `run` field, restrict it to a fixed set of known-safe values so a caller cannot supply arbitrary shell text at all. This is the strongest control the engine offers — prefer it over any downstream mitigation.
|
||||
|
||||
```yaml
|
||||
inputs:
|
||||
target:
|
||||
type: string
|
||||
enum: [staging, production] # caller cannot inject arbitrary text
|
||||
```
|
||||
|
||||
- **Keep unconstrained values out of `run`.** If a value cannot be constrained to an allowlist — most agent/`prompt` output — do not interpolate it into a `run` field. Branch on it with `if`/`switch` against fixed conditions, or act on it in a `command`/`prompt` step rather than a shell command built from it.
|
||||
- **Quoting is not a security boundary.** Surrounding a substitution with quotes (`'{{ inputs.x }}'`) helps the shell treat a *trusted* value as a single argument and avoids word-splitting on spaces, but a value that itself contains the matching quote character can still break out and inject shell syntax. Quote for correctness on constrained values; never rely on quoting to make an *unconstrained* substitution safe.
|
||||
- **Gates do not inspect the next step, and `message` is printed verbatim.** A `gate` step renders only its own `message`/`show_file` — it does not display, resolve, or sanitise the command that follows it, and approval never neutralises an injectable interpolation. Do **not** interpolate raw untrusted data into `message`: it is printed as-is with no control-character stripping, so agent or caller output could inject terminal/ANSI escapes that alter or hide the approval prompt. Keep `message` to trusted, constrained text, and surface untrusted material for review via `show_file` instead — its path and contents are control/ANSI-stripped before display.
|
||||
|
||||
A `shell` step is an arbitrary-command primitive by design; these practices reduce exposure and keep *which* command runs under the author's control, but they do not eliminate the risk of interpolating values you do not fully control.
|
||||
|
||||
## Shell Step Environment Variables
|
||||
|
||||
Shell steps automatically receive the following environment variables:
|
||||
@@ -578,64 +528,6 @@ Each workflow run persists its state at `.specify/workflows/runs/<run_id>/`:
|
||||
|
||||
This enables `specify workflow resume` to continue from the exact step where a run was paused (e.g., at a gate) or failed.
|
||||
|
||||
### Gate Verdict Inputs
|
||||
|
||||
`verdict_input` binds a gate's verdict to a named workflow input. The input must be declared in the workflow's `inputs` block; `specify workflow validate` reports an undeclared reference.
|
||||
|
||||
`verdict_input` is not supported inside a `fan-out` template. Fan-out items
|
||||
share workflow inputs, while workflow state can represent only one paused
|
||||
gate. Place a gate before the fan-out to approve the whole batch, or after a
|
||||
fan-in to review the aggregated results.
|
||||
|
||||
**Input value semantics:**
|
||||
|
||||
| Value | Behavior |
|
||||
|---|---|
|
||||
| Non-empty string, matches an option (case-insensitive) | Gate auto-decides; `output.choice` is set to the configured option spelling |
|
||||
| Non-empty string, no match | Gate fails immediately |
|
||||
| Non-string | Gate fails immediately |
|
||||
| Missing or empty | Gate prompts on a TTY; pauses otherwise |
|
||||
|
||||
**Default value semantics:** A non-empty `default` is consumed as a verdict on the first run — matching an option auto-decides the gate, not matching fails it immediately.
|
||||
|
||||
```yaml
|
||||
inputs:
|
||||
spec_verdict:
|
||||
type: string
|
||||
default: ""
|
||||
steps:
|
||||
- id: review-spec
|
||||
type: gate
|
||||
message: "Approve the specification?"
|
||||
options: [approve, reject]
|
||||
on_reject: retry
|
||||
verdict_input: spec_verdict
|
||||
```
|
||||
|
||||
Supply a verdict when resuming:
|
||||
|
||||
```bash
|
||||
specify workflow resume <run_id> --input spec_verdict=approve
|
||||
```
|
||||
|
||||
For `on_reject: retry`, a bound reject verdict is consumed before the gate
|
||||
pauses: the named stored input is reset to `""`. A later resume therefore
|
||||
prompts or pauses again until another verdict is supplied. Approve, abort, and
|
||||
skip outcomes leave the input unchanged.
|
||||
|
||||
Because of that reset, a verdict input used with `on_reject: retry` must accept
|
||||
`""`. If it declares an `enum`, include the empty string — otherwise the reset
|
||||
value violates the input's own `enum` and the run can no longer be resumed with
|
||||
any input. `specify workflow add` reports this as a validation error.
|
||||
|
||||
```yaml
|
||||
inputs:
|
||||
spec_verdict:
|
||||
type: string
|
||||
enum: ["", approve, reject]
|
||||
default: ""
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### What happens when a workflow hits a gate step?
|
||||
|
||||
@@ -55,8 +55,6 @@
|
||||
href: concepts/spec-persistence.md
|
||||
- name: Handling Complex Features
|
||||
href: concepts/complex-features.md
|
||||
- name: Spec of Specs
|
||||
href: concepts/spec-of-specs.md
|
||||
|
||||
# Development workflows
|
||||
- name: Development
|
||||
|
||||
+98
-168
@@ -12,7 +12,7 @@
|
||||
| **CLI Tool — pin a version** | `specify self upgrade --tag vX.Y.Z[suffix]` | Upgrade to a specific release tag instead of the latest stable. Suffixes are limited to dev, alpha/beta/rc, and/or build metadata forms. |
|
||||
| **CLI Tool — manual fallback** | `uv tool install specify-cli --force --from git+https://github.com/github/spec-kit.git@vX.Y.Z` | When `specify self upgrade` isn't available (older installs) or when you want explicit control. |
|
||||
| **CLI Tool — manual fallback (pipx)** | `pipx install --force git+https://github.com/github/spec-kit.git@vX.Y.Z` | Same as above, for pipx installs. |
|
||||
| **Project Files** | Run `specify integration upgrade <key>`, then `specify extension update` | Refresh installed integration files and extensions in your project |
|
||||
| **Project Files** | `specify init --here --force --integration <your-agent>` | Update slash commands, templates, and scripts in your project |
|
||||
| **Both** | Run CLI upgrade, then project update | Recommended for major version updates |
|
||||
|
||||
---
|
||||
@@ -89,94 +89,91 @@ specify self check
|
||||
|
||||
## Part 2: Updating Project Files
|
||||
|
||||
When Spec Kit releases new features (like new slash commands, updated templates, or extension changes), you need to refresh the Spec Kit files that were installed into your project.
|
||||
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?
|
||||
|
||||
For existing Spec Kit projects, use the manifest-aware upgrade path first:
|
||||
Running `specify init --here --force` will update:
|
||||
|
||||
- ✅ **Integration command/skill files** (`.claude/skills/`, `.github/prompts/`, `.agents/skills/`, etc.)
|
||||
- ✅ **Managed shared scripts and templates** (`.specify/scripts/`, `.specify/templates/`) when they are unchanged from the previous managed copy
|
||||
- ✅ **Installed extensions** when you run `specify extension update`
|
||||
|
||||
The integration upgrade command uses the install manifest to detect local edits. If a managed integration file was modified after install, the command stops and asks you to inspect the change or rerun with `--force`.
|
||||
- ✅ **Slash command files** (`.claude/commands/`, `.github/prompts/`, etc.)
|
||||
- ✅ **Script files** (`.specify/scripts/`) — **only with `--force`**; without it, only missing files are added
|
||||
- ✅ **Template files** (`.specify/templates/`) — **only with `--force`**; without it, only missing files are added
|
||||
- ✅ **Shared memory files** (`.specify/memory/`) - **⚠️ See warnings below**
|
||||
|
||||
### What stays safe?
|
||||
|
||||
These files are **never touched** by the manifest-aware integration/extension upgrade path:
|
||||
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 constitution** (`.specify/memory/constitution.md`) when using `specify integration upgrade`
|
||||
- ✅ **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.
|
||||
|
||||
### 1. Check installed integrations
|
||||
### Update command
|
||||
|
||||
Run this inside your project directory:
|
||||
|
||||
```bash
|
||||
specify integration status
|
||||
```
|
||||
|
||||
This reports the default integration, all installed integrations, and any modified or missing managed files. You can also inspect `.specify/integration.json`; installed integrations are listed under `installed_integrations`.
|
||||
|
||||
### 2. Upgrade each installed integration
|
||||
|
||||
Run this inside your project directory:
|
||||
|
||||
```bash
|
||||
specify integration upgrade <key>
|
||||
```
|
||||
|
||||
Replace `<key>` with an installed integration key such as `copilot`, `claude`, or `codex`. In projects with multiple installed integrations, run the command once per installed key.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
specify integration upgrade claude
|
||||
specify integration upgrade codex
|
||||
```
|
||||
|
||||
See the [integration reference](reference/integrations.md#upgrade-an-integration) for options such as `--script`, `--integration-options`, and `--force`.
|
||||
|
||||
### 3. Update installed extensions
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
specify extension update
|
||||
```
|
||||
|
||||
With no extension argument, this updates all installed extensions. Use `specify extension update <extension-id-or-name>` to update only one extension. See the [extensions reference](reference/extensions.md#update-extensions) for details.
|
||||
|
||||
### Fallback: re-run init
|
||||
|
||||
If a project predates manifests, has missing integration metadata, or needs a broader recovery, you can still re-run init:
|
||||
|
||||
```bash
|
||||
specify init --here --force --integration <your-agent>
|
||||
```
|
||||
|
||||
Use this as an escape hatch rather than the default project-file upgrade path. It refreshes the selected integration and shared project scaffolding, but it does not use the same per-integration manifest checks before overwriting files.
|
||||
Replace `<your-agent>` with your AI coding agent. Refer to this list of [Supported AI Coding Agent Integrations](reference/integrations.md)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
specify init --here --force --integration 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. It also **overwrites shared infrastructure files** (`.specify/scripts/` and `.specify/templates/`) with the latest versions from the installed Spec Kit release.
|
||||
|
||||
Without `--force`, shared infrastructure files that already exist are skipped — the CLI will print a warning listing the skipped files so you know which ones were not updated.
|
||||
|
||||
**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 and memory customizations
|
||||
### 1. Constitution file will be overwritten
|
||||
|
||||
`specify integration upgrade <key>` does not update `.specify/memory/constitution.md`.
|
||||
**Known issue:** `specify init --here --force` currently overwrites `.specify/memory/constitution.md` with the default template, erasing any customizations you made.
|
||||
|
||||
The fallback `specify init --here --force --integration <your-agent>` path also preserves an existing `.specify/memory/constitution.md`; if the file is missing, init creates it from the current constitution template. You do not need a constitution backup/restore step for the manifest-aware upgrade path.
|
||||
**Workaround:**
|
||||
|
||||
As with any broad fallback refresh, commit or back up local customizations before using `init --here --force` so you can review the resulting diff.
|
||||
```bash
|
||||
# 1. Back up your constitution before upgrading
|
||||
cp .specify/memory/constitution.md .specify/memory/constitution-backup.md
|
||||
|
||||
### 2. Custom integration, script, or template modifications
|
||||
# 2. Run the upgrade
|
||||
specify init --here --force --integration copilot
|
||||
|
||||
`specify integration upgrade <key>` blocks when manifest-tracked integration files were modified locally, unless you pass `--force`.
|
||||
# 3. Restore your customized constitution
|
||||
mv .specify/memory/constitution-backup.md .specify/memory/constitution.md
|
||||
```
|
||||
|
||||
Shared scripts and templates are refreshed when they still match the previously recorded managed copy. Local customizations are preserved unless you explicitly use a force/refresh option that overwrites them. If you customized files in `.specify/scripts/` or `.specify/templates/`, commit or back them up first:
|
||||
Or use git to restore it:
|
||||
|
||||
```bash
|
||||
# After upgrade, restore from git history
|
||||
git restore .specify/memory/constitution.md
|
||||
```
|
||||
|
||||
### 2. Custom script or template modifications
|
||||
|
||||
If you customized files in `.specify/scripts/` or `.specify/templates/`, the `--force` flag will overwrite them. Back them up first:
|
||||
|
||||
```bash
|
||||
# Back up custom templates and scripts
|
||||
@@ -195,87 +192,21 @@ Some IDE-based agents (like Kilo Code, Cline) may show **duplicate slash command
|
||||
**Example for Kilo Code:**
|
||||
|
||||
```bash
|
||||
# List current and legacy Kilo command folders
|
||||
ls -la .kilo/commands/
|
||||
ls -la .kilocode/workflows/
|
||||
# Navigate to the agent's commands folder
|
||||
cd .kilocode/workflows/
|
||||
|
||||
# List files and identify duplicates
|
||||
ls -la
|
||||
|
||||
# Delete old versions (example filenames - yours may differ)
|
||||
rm .kilocode/workflows/speckit.specify-old.md
|
||||
rm .kilocode/workflows/speckit.plan-v1.md
|
||||
rm speckit.specify-old.md
|
||||
rm speckit.plan-v1.md
|
||||
```
|
||||
|
||||
Restart your IDE to refresh the command list.
|
||||
|
||||
---
|
||||
|
||||
## Behavior change: `/constitution` no longer propagates into templates
|
||||
|
||||
The `/constitution` command ([#3790](https://github.com/github/spec-kit/pull/3790)) is scoped to
|
||||
its own artifact. It updates
|
||||
`.specify/memory/constitution.md` and writes a Sync Impact Report, and **no longer edits**
|
||||
`plan-template.md`, `spec-template.md`, `tasks-template.md`, installed command files, or
|
||||
guidance docs.
|
||||
|
||||
### Why
|
||||
|
||||
Spec Kit uses **runtime resolution**: `plan`, `tasks`, and `analyze` read
|
||||
`.specify/memory/constitution.md` live on every run, and `analyze` is the dedicated drift
|
||||
checker. The governed templates carry a pointer, not a copy — `plan-template.md` ships
|
||||
`[Gates determined based on constitution file]`, and `/plan` fills that section from the live
|
||||
constitution each run. Propagation duplicated the single source of truth and fought the
|
||||
preset/override composition system (a `replace` preset shadows an edited core template).
|
||||
|
||||
More broadly, presets and extensions — not in-place file edits — are how Spec Kit now governs
|
||||
shared assets. Composing policy through the resolution stack keeps it centrally owned, versioned,
|
||||
and auditable across repositories, instead of frozen into per-repo copies no core team can see.
|
||||
|
||||
### Is this a breaking change for existing projects?
|
||||
|
||||
**No — your workflow keeps working.** You would only notice a difference if you relied on
|
||||
`/constitution` editing those files in place. The templates are scaffolds, not authorities. When you
|
||||
run `/plan`, it copies the template into a per-feature `plan.md` and re-derives the Constitution
|
||||
Check from the live constitution; `/analyze` validates against it. Even if a previous
|
||||
`/constitution` run materialized concrete gate text into `.specify/templates/plan-template.md`,
|
||||
the live constitution remains the source of truth at runtime.
|
||||
|
||||
On a **non-forced upgrade**, a materialized template is *preserved* (its hash diverges from the
|
||||
recorded managed copy, so the refresh treats it as a customization and does not overwrite it).
|
||||
Nothing regresses.
|
||||
|
||||
### Optional cleanup — return to the runtime pointer
|
||||
|
||||
A frozen, pre-filled Constitution Check is a slightly misleading scaffold and can bias the first
|
||||
`/plan` pass. To move fully back to runtime resolution, reset the section body in
|
||||
`.specify/templates/plan-template.md` to the pointer:
|
||||
|
||||
```text
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
[Gates determined based on constitution file]
|
||||
```
|
||||
|
||||
Leave the rest of the file untouched. This is cleanup, not a required migration.
|
||||
|
||||
### Keeping the old behavior (opt-in)
|
||||
|
||||
If your team treats the materialized templates as **reviewed, committed artifacts** and wants
|
||||
`/constitution` to keep propagating, install the bundled **`constitution-sync`** preset:
|
||||
|
||||
```bash
|
||||
specify preset add constitution-sync
|
||||
```
|
||||
|
||||
It wraps the core `/constitution` command and re-adds the propagation pass. It does **not** edit
|
||||
versioned preset- or extension-provided templates or command files (those are owned by their
|
||||
packages and are recomposed on reconciliation). Note that this edit-in-place propagation model
|
||||
conflicts with the composition model used by the rest of the SDD commands when they are
|
||||
preset/extension-managed — see the "Interaction with the resolution stack" section in
|
||||
`presets/constitution-sync/README.md` for the tradeoffs and when to prefer the default instead.
|
||||
|
||||
---
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario 1: "I just want new slash commands"
|
||||
@@ -284,29 +215,29 @@ preset/extension-managed — see the "Interaction with the resolution stack" sec
|
||||
# Upgrade CLI (auto-detects uv tool vs pipx install)
|
||||
specify self upgrade
|
||||
|
||||
# Inspect installed integrations
|
||||
specify integration status
|
||||
|
||||
# Update project files to get new commands
|
||||
specify integration upgrade <key>
|
||||
specify extension update
|
||||
specify init --here --force --integration copilot
|
||||
|
||||
# Restore your constitution if customized
|
||||
git restore .specify/memory/constitution.md
|
||||
```
|
||||
|
||||
### Scenario 2: "I customized templates and constitution"
|
||||
|
||||
```bash
|
||||
# 1. Commit or back up customizations
|
||||
git status
|
||||
# 1. Back up customizations
|
||||
cp .specify/memory/constitution.md /tmp/constitution-backup.md
|
||||
cp -r .specify/templates /tmp/templates-backup
|
||||
|
||||
# 2. Upgrade CLI
|
||||
specify self upgrade
|
||||
|
||||
# 3. Use the manifest-aware project update first
|
||||
specify integration upgrade <key>
|
||||
specify extension update
|
||||
# 3. Update project
|
||||
specify init --here --force --integration copilot
|
||||
|
||||
# 4. If the upgrade reports modified managed files, inspect the diff before using --force
|
||||
# 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"
|
||||
@@ -314,12 +245,14 @@ specify extension update
|
||||
This happens with IDE-based agents (Kilo Code, Cline, etc.).
|
||||
|
||||
```bash
|
||||
# For Kilo Code, inspect both current and legacy command folders
|
||||
ls -la .kilo/commands/
|
||||
ls -la .kilocode/workflows/
|
||||
# Find the agent folder (example: .kilocode/workflows/)
|
||||
cd .kilocode/workflows/
|
||||
|
||||
# List all files
|
||||
ls -la
|
||||
|
||||
# Delete old command files
|
||||
rm .kilocode/workflows/speckit.old-command-name.md
|
||||
rm speckit.old-command-name.md
|
||||
|
||||
# Restart your IDE
|
||||
```
|
||||
@@ -329,14 +262,14 @@ rm .kilocode/workflows/speckit.old-command-name.md
|
||||
The git extension is now opt-in, so upgrades do not install it unless you add it explicitly.
|
||||
|
||||
```bash
|
||||
# Upgrade CLI
|
||||
specify self upgrade
|
||||
# Manually back up files you customized
|
||||
cp .specify/memory/constitution.md .specify/memory/constitution.backup.md
|
||||
|
||||
# Refresh integration files and installed extensions
|
||||
specify integration upgrade <key>
|
||||
specify extension update
|
||||
# Run upgrade
|
||||
specify init --here --force --integration copilot
|
||||
|
||||
# The git extension is not added unless you run `specify extension add git`
|
||||
# Restore customizations
|
||||
mv .specify/memory/constitution.backup.md .specify/memory/constitution.md
|
||||
```
|
||||
|
||||
If you later decide you want the git extension's commands and hooks, install it explicitly:
|
||||
@@ -371,7 +304,7 @@ Alternatively, run the `/speckit.specify` command which creates `.specify/featur
|
||||
2. **For CLI-based agents**, verify files exist:
|
||||
|
||||
```bash
|
||||
ls -la .claude/skills/ # Claude Code
|
||||
ls -la .claude/commands/ # Claude Code
|
||||
ls -la .gemini/commands/ # Gemini
|
||||
ls -la .cursor/skills/ # Cursor
|
||||
ls -la .pi/prompts/ # Pi Coding Agent
|
||||
@@ -382,21 +315,19 @@ Alternatively, run the `/speckit.specify` command which creates `.specify/featur
|
||||
- Codex requires `CODEX_HOME` environment variable
|
||||
- Some agents need workspace restart or cache clearing
|
||||
|
||||
### "Will init overwrite my constitution customizations?"
|
||||
### "I lost my constitution customizations"
|
||||
|
||||
Current `specify init --here --force` preserves an existing `.specify/memory/constitution.md`; it creates the file from the template only when it is missing.
|
||||
|
||||
If you previously lost constitution changes through an older workflow or manual replacement, restore from git or backup:
|
||||
**Fix:** Restore from git or backup:
|
||||
|
||||
```bash
|
||||
# If you committed the customized constitution
|
||||
# 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:** Use `specify integration upgrade <key>` for routine project-file updates. If you need the fallback `specify init --here --force` path, commit first so you can review the full diff afterward.
|
||||
**Prevention:** Always commit or back up `constitution.md` before upgrading.
|
||||
|
||||
### "Warning: Current directory is not empty"
|
||||
|
||||
@@ -420,10 +351,10 @@ This warning appears when you run `specify init --here` (or `specify init .`) in
|
||||
|
||||
Only Spec Kit infrastructure files:
|
||||
|
||||
- Agent command/skill files (`.claude/skills/`, `.github/prompts/`, etc.)
|
||||
- Agent command files (`.claude/commands/`, `.github/prompts/`, etc.)
|
||||
- Scripts in `.specify/scripts/`
|
||||
- Templates in `.specify/templates/`
|
||||
- Missing memory files such as `.specify/memory/constitution.md` may be created from templates; an existing constitution is preserved
|
||||
- Memory files in `.specify/memory/` (including constitution)
|
||||
|
||||
**What stays untouched:**
|
||||
|
||||
@@ -434,7 +365,7 @@ Only Spec Kit infrastructure files:
|
||||
|
||||
**How to respond:**
|
||||
|
||||
- **Type `y` and press Enter** - Proceed with the merge when using the fallback init path
|
||||
- **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:
|
||||
|
||||
@@ -444,11 +375,11 @@ Only Spec Kit infrastructure files:
|
||||
|
||||
**When you see this warning:**
|
||||
|
||||
- ✅ **Expected** when using the fallback init path in an existing Spec Kit project
|
||||
- ✅ **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 using the fallback init path, commit your current work so any refreshed files are easy to review or restore.
|
||||
**Prevention tip:** Before upgrading, commit or back up your `.specify/memory/constitution.md` if you customized it.
|
||||
|
||||
### "CLI upgrade doesn't seem to work"
|
||||
|
||||
@@ -487,15 +418,14 @@ 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 later as a fallback recovery path.
|
||||
**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
|
||||
- **Routine project-file upgrades:** `specify integration upgrade <key>` and `specify extension update`
|
||||
- **Fallback recovery:** `specify init --here --force` when integration metadata is missing or the manifest-aware path cannot be used
|
||||
- **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/`, `.pi/prompts/`, `.omp/commands/`, etc.). Your AI coding agent reads these command files directly—no need to run `specify` again.
|
||||
@@ -509,7 +439,7 @@ Once you've run `specify init`, the slash commands (like `/speckit.specify`, `/s
|
||||
ls -la .github/prompts/
|
||||
|
||||
# For Claude
|
||||
ls -la .claude/skills/
|
||||
ls -la .claude/commands/
|
||||
|
||||
# For Pi
|
||||
ls -la .pi/prompts/
|
||||
|
||||
@@ -40,25 +40,12 @@ requires:
|
||||
required: boolean # Optional, default: false
|
||||
|
||||
provides:
|
||||
commands: # At least one of commands/templates/scripts/hooks/events required
|
||||
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, same pattern as name; namespace must match extension.id and must not shadow core or installed extension commands
|
||||
|
||||
templates: # Optional, array of declared templates. Always resolve
|
||||
# as "replace" -- 'strategy' is not an authorable field here.
|
||||
- name: string # Required, pattern: ^[a-z0-9-]+$
|
||||
file: string # Required, relative path to template file
|
||||
description: string # Optional
|
||||
|
||||
scripts: # Optional, array of declared scripts. Always resolve
|
||||
# as "replace" -- 'strategy' is not an authorable field here.
|
||||
- name: string # Required, pattern: ^[a-z0-9-]+$
|
||||
file: string # Required, relative path to script file
|
||||
description: string # Optional
|
||||
runtimes: [string] # Optional, subset of: bash, powershell, python
|
||||
|
||||
config: # Optional, array of config files
|
||||
- name: string # Config file name
|
||||
template: string # Template file path
|
||||
@@ -124,29 +111,6 @@ defaults: # Optional, default configuration values
|
||||
- **Examples**: `speckit.jira.specstoissues`, `speckit.linear.sync`
|
||||
- **Invalid**: `jira.specstoissues`, `speckit.command`, `speckit.jira.CreateIssues`
|
||||
|
||||
#### `provides.templates[].name` / `provides.scripts[].name`
|
||||
|
||||
- **Type**: string
|
||||
- **Pattern**: `^[a-z0-9-]+$`
|
||||
- **Description**: Unlike commands, templates and scripts are not invoked by
|
||||
name, so they use the same plain slug pattern as `extension.id` rather than
|
||||
the namespaced command pattern.
|
||||
- **Examples**: `myext-template`, `myext-collect`
|
||||
|
||||
#### `provides.templates[].strategy` / `provides.scripts[].strategy`
|
||||
|
||||
- Not an authorable field. Extension-contributed templates and scripts are
|
||||
always resolved as `replace`; a manifest that includes a `strategy` key on
|
||||
one of these entries is rejected with a `ValidationError`. Composable
|
||||
strategies (`wrap`/`prepend`/`append`) are preset-only.
|
||||
|
||||
#### `provides.scripts[].runtimes`
|
||||
|
||||
- **Type**: array of strings
|
||||
- **Values**: `bash`, `powershell`, `python`
|
||||
- **Description**: Declares which runtimes the script supports. Purely
|
||||
informational metadata — it is not used to select or invoke the script.
|
||||
|
||||
#### `hooks`
|
||||
|
||||
- **Type**: object
|
||||
@@ -179,8 +143,6 @@ manifest.version # str: Version
|
||||
manifest.description # str: Description
|
||||
manifest.requires_speckit_version # str: Required spec-kit version
|
||||
manifest.commands # List[Dict]: Command definitions
|
||||
manifest.templates # List[Dict]: Declared template definitions
|
||||
manifest.scripts # List[Dict]: Declared script definitions
|
||||
manifest.hooks # Dict: Hook definitions
|
||||
```
|
||||
|
||||
|
||||
@@ -177,16 +177,9 @@ Compatibility requirements.
|
||||
|
||||
What the extension provides.
|
||||
|
||||
**Optional sub-fields:**
|
||||
**Optional sub-fields**:
|
||||
|
||||
- `commands`: Array of command objects
|
||||
- `templates`: Array of template objects
|
||||
- `scripts`: Array of script objects
|
||||
|
||||
`hooks` and `events` are separate top-level manifest fields (siblings of
|
||||
`provides`, not nested under it — see [`hooks`](#hooks) below). At least one
|
||||
of `provides.commands`, `provides.templates`, `provides.scripts`, `hooks`, or
|
||||
`events` is required.
|
||||
- `commands`: Array of command objects (at least one command or hook is required)
|
||||
|
||||
**Command object**:
|
||||
|
||||
@@ -195,21 +188,6 @@ of `provides.commands`, `provides.templates`, `provides.scripts`, `hooks`, or
|
||||
- `description`: Command description (optional)
|
||||
- `aliases`: Alternative command names (optional, array; each must match `speckit.{ext-id}.{command}`)
|
||||
|
||||
**Template object**:
|
||||
|
||||
- `name`: Template name (lowercase, alphanumeric, hyphens — e.g. `myext-template`)
|
||||
- `file`: Path to template file (relative to extension root)
|
||||
- `description`: Template description (optional)
|
||||
|
||||
**Script object**:
|
||||
|
||||
- `name`: Script name (lowercase, alphanumeric, hyphens — e.g. `myext-collect`)
|
||||
- `file`: Path to script file (relative to extension root)
|
||||
- `description`: Script description (optional)
|
||||
- `runtimes`: Runtimes the script supports (optional, array; subset of `bash`, `powershell`, `python` — informational only, not used to select or invoke the script)
|
||||
|
||||
Extension-provided templates and scripts always resolve as `replace`; a manifest that includes a `strategy` key on one of these entries is rejected with a `ValidationError`. Composable strategies (`wrap`/`prepend`/`append`) are preset-only.
|
||||
|
||||
### Optional Fields
|
||||
|
||||
#### `hooks`
|
||||
@@ -274,7 +252,6 @@ Use standard Markdown with special placeholders:
|
||||
|
||||
- `$ARGUMENTS`: User-provided arguments
|
||||
- `{SCRIPT}`: Replaced with script path during registration
|
||||
- `__SPECKIT_COMMAND_<NAME>__`: Replaced with the invocation of another command, rendered using the active integration's separator (see [Referencing other commands](#referencing-other-commands))
|
||||
|
||||
**Example**:
|
||||
|
||||
@@ -290,40 +267,6 @@ echo "Running with args: $args"
|
||||
```
|
||||
````
|
||||
|
||||
### Referencing other commands
|
||||
|
||||
A command body is a *template* that Spec Kit renders once per agent. Different agents invoke commands with different surface syntax — for example `/speckit.plan` (dot separator) or `/speckit-plan` (hyphen separator). Some agents also use different prefixes in skills mode (e.g. Kimi `/skill:speckit-plan`, Codex/ZCode `$speckit-plan`). So when you reference a sibling command from a body, **do not hard-code a literal invocation** like `/speckit.my-ext.prepare`. A literal is correct for exactly one agent and breaks on the rest.
|
||||
|
||||
Instead use the agent-neutral token `__SPECKIT_COMMAND_<NAME>__`. Spec Kit resolves it to a `/speckit<separator>...` invocation using the active integration's `invoke_separator` (and integrations may post-process that further in skills output).
|
||||
|
||||
Encode the command name in upper case, dropping the `speckit.` prefix and turning each dotted segment separator into an underscore:
|
||||
|
||||
| Command file | Token |
|
||||
| --- | --- |
|
||||
| `speckit.plan.md` | `__SPECKIT_COMMAND_PLAN__` |
|
||||
| `speckit.bug.fix.md` | `__SPECKIT_COMMAND_BUG_FIX__` |
|
||||
| `speckit.git.commit.md` | `__SPECKIT_COMMAND_GIT_COMMIT__` |
|
||||
|
||||
The resolver maps each underscore back to the active agent's separator, so use tokens to reference commands whose name segments are single words. (Command names are dotted segments like `git.commit`; the token scheme rebuilds those dots and does not carry hyphens within a segment.)
|
||||
|
||||
**Example** — a command body that points the user at the next step:
|
||||
|
||||
```markdown
|
||||
Once the assessment exists, the next step is `__SPECKIT_COMMAND_BUG_FIX__ slug=<slug>`.
|
||||
```
|
||||
|
||||
This renders as `/speckit.bug.fix slug=<slug>` for a slash-based agent, `/speckit-bug-fix slug=<slug>` for a skills-based agent, and so on — the author writes it once and it stays portable. The first-party `bug` and `git` extensions use this token exclusively; see `extensions/bug/commands/` for working examples.
|
||||
|
||||
> **Current limitation — skills mode.** Token resolution runs in the
|
||||
> command-rendering path (`CommandRegistrar`), so it applies when an extension
|
||||
> installs *command files*. It does **not** yet run when an extension is
|
||||
> registered as *skills* for a skills-based agent: `_register_extension_skills`
|
||||
> resolves placeholders and post-processes content but never calls
|
||||
> `resolve_command_refs`, so a `__SPECKIT_COMMAND_<NAME>__` token reaches
|
||||
> agents such as Codex, ZCode, and Kimi verbatim in that mode. Until that
|
||||
> rendering step lands, prefer the token for command-file extensions and avoid
|
||||
> relying on it inside skill bodies destined for skills-based agents.
|
||||
|
||||
### Script Path Rewriting
|
||||
|
||||
Extension commands use relative paths that get rewritten during registration:
|
||||
|
||||
@@ -151,7 +151,7 @@ To submit your extension to the community catalog, file a new issue using the **
|
||||
|
||||
### What Happens After You Submit
|
||||
|
||||
1. A maintainer reviews the issue during issue triage and applies the `extension-submission` label, which starts the automated catalog validation. On this public repository, contributors cannot apply that label themselves, so there is nothing to label or re-request — the issue simply waits in triage.
|
||||
1. Your issue is automatically labeled and assigned to a maintainer for review
|
||||
2. A maintainer verifies that the catalog entry is complete and correctly formatted
|
||||
3. Once approved, the maintainer adds your extension to `extensions/catalog.community.json` and the Community Extensions table in the README
|
||||
4. Your extension becomes discoverable via `specify extension search`
|
||||
|
||||
@@ -2,55 +2,55 @@
|
||||
|
||||
This bundled extension manages the **coding agent context/instruction file** (e.g. `CLAUDE.md`, `.github/copilot-instructions.md`, `AGENTS.md`, `GEMINI.md`, …) for the active integration.
|
||||
|
||||
It owns the lifecycle of the managed section delimited by the configurable start/end markers (defaults: `<!-- SPECKIT START -->` / `<!-- SPECKIT END -->`). For `.mdc` files, it also ensures the YAML frontmatter (the metadata block at the top of the file) contains `alwaysApply: true`. Otherwise, everything outside the managed section is untouched.
|
||||
|
||||
> NOTE: Spec Kit itself never touches your agent context file. This extension is the only thing that does, and it's opt-in: install it if you want the block kept in sync, skip it if you'd rather manage that file yourself.
|
||||
It owns the lifecycle of the managed section delimited by the configurable start/end markers (defaults: `<!-- SPECKIT START -->` / `<!-- SPECKIT END -->`).
|
||||
|
||||
## Why an extension?
|
||||
|
||||
Not every Spec Kit user wants Spec Kit to write into the coding agent's context file. Keeping this behavior in a dedicated, **opt-in** extension lets users:
|
||||
|
||||
- **Choose whether to install it at all** - `specify init` does **not** install it. Add it explicitly when you want Spec Kit to manage the agent context file; when it is absent, the file is never modified, and when it is disabled, its automatic hooks do not run.
|
||||
- **Customize the markers** by editing `.specify/extensions/agent-context/agent-context-config.yml` ([agent-context-config.yml](./agent-context-config.yml) in this repo) - the bundled scripts honor the `context_markers` value.
|
||||
- **Choose whether to install it at all** — `specify init` does not install it. Add it explicitly when you want Spec Kit to manage the agent context file; if it is absent or disabled, Spec Kit never creates or modifies that file.
|
||||
- **Customize the markers** by editing `.specify/extensions/agent-context/agent-context-config.yml` — the bundled scripts honor the `context_markers` value.
|
||||
- **Synchronize multiple agent anchors** by setting `context_files` when a project intentionally uses more than one coding agent context file, such as `AGENTS.md` and `CLAUDE.md`.
|
||||
- **Refresh on demand** by running the `speckit.agent-context.update` command in your agent, or automatically through the hooks declared in [extension.yml](./extension.yml) (`after_specify`, `after_plan`).
|
||||
|
||||
## Installation
|
||||
|
||||
To install the extension, from the root of an initialized Spec Kit project, run:
|
||||
|
||||
```bash
|
||||
specify extension add agent-context
|
||||
```
|
||||
|
||||
## Disabling
|
||||
|
||||
```bash
|
||||
specify extension disable agent-context
|
||||
|
||||
# Re-enable it
|
||||
specify extension enable agent-context
|
||||
```
|
||||
|
||||
While this extension is disabled (or not installed), nothing in Spec Kit creates, updates, or removes the managed block - the `__CONTEXT_FILE__` placeholder in any template is left as-is, and the extension's own config is never read.
|
||||
- **Refresh on demand** by running the `speckit.agent-context.update` command in your agent, or automatically through the hooks declared in `extension.yml` (`after_specify`, `after_plan`). Invoke it using your agent's slash-command separator — `/speckit.agent-context.update` for dot-separator agents or `/speckit-agent-context-update` for hyphen-separator agents (e.g. Forge, Cline).
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------------ | --------------------------------------------------------------------------------- |
|
||||
| `speckit.agent-context.update` | Refresh the managed section in the agent context file with the current plan path. |
|
||||
The command ID below is canonical. When invoking it as a slash command, use your agent's separator: `/speckit.agent-context.update` for dot-separator agents or `/speckit-agent-context-update` for hyphen-separator agents (e.g. Forge, Cline).
|
||||
|
||||
> NOTE: The command ID above is canonical. Invoke it using the syntax for your integration: `/speckit.agent-context.update` for dot-command integrations; `/speckit-agent-context-update` for hyphen/skills integrations (including Forge and Cline); `$speckit-agent-context-update` for Codex or ZCode in skills mode; or `/skill:speckit-agent-context-update` for Kimi.
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `speckit.agent-context.update` | Refresh the managed section in the agent context file with the current plan path. |
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration flows through the extension's own config file at `.specify/extensions/agent-context/agent-context-config.yml` ([agent-context-config.yml](./agent-context-config.yml) in the repo).
|
||||
All configuration flows through the extension's own config file at
|
||||
`.specify/extensions/agent-context/agent-context-config.yml`:
|
||||
|
||||
```yaml
|
||||
# Path to the coding agent context file managed by this extension
|
||||
context_file: CLAUDE.md
|
||||
|
||||
# Optional list of coding agent context files to manage together.
|
||||
# When non-empty, this takes precedence over context_file.
|
||||
context_files:
|
||||
- AGENTS.md
|
||||
- CLAUDE.md
|
||||
|
||||
# Delimiters for the managed Spec Kit section
|
||||
context_markers:
|
||||
start: "<!-- SPECKIT START -->"
|
||||
end: "<!-- SPECKIT END -->"
|
||||
```
|
||||
|
||||
- `context_file` — the project-relative path to the coding agent context file. When empty, the bundled update scripts self-seed it by looking up the active integration's key in this extension's own `agent-context-defaults.json` map. The Specify CLI is never consulted.
|
||||
- `context_files` — optional project-relative paths to multiple coding agent context files. When non-empty, the list takes precedence over `context_file`. Absolute paths, backslash separators, and `..` path segments are rejected.
|
||||
- `context_markers.start` / `.end` — the delimiters around the managed section. Edit these to use custom markers.
|
||||
|
||||
## Requirements
|
||||
|
||||
The bundled update scripts require **Python 3** with **PyYAML** for YAML/upsert processing (PowerShell can also use `ConvertFrom-Yaml` when available).
|
||||
|
||||
PyYAML ships with the `specify` CLI and is normally available via the same `python3` interpreter. If a hook reports _"PyYAML is required … not available in the current Python environment"_, it means the system `python3` differs from the one used to install Spec Kit. To resolve, run:
|
||||
PyYAML ships with the `specify` CLI and is normally available via the same `python3` interpreter. If a hook reports *"PyYAML is required … not available in the current Python environment"*, it means the system `python3` differs from the one used to install Spec Kit. To resolve, run:
|
||||
|
||||
```bash
|
||||
pip install pyyaml
|
||||
@@ -58,6 +58,10 @@ pip install pyyaml
|
||||
/path/to/speckit-python -m pip install pyyaml
|
||||
```
|
||||
|
||||
## Issues
|
||||
## Disable
|
||||
|
||||
For any other issues, please create an issue in the [official GitHub repo](https://github.com/github/spec-kit/issues).
|
||||
```bash
|
||||
specify extension disable agent-context
|
||||
```
|
||||
|
||||
When disabled (or never installed), Spec Kit performs no agent context file creation, updates, or removal — the extension's bundled scripts are the only code that ever touches the managed section. The Specify CLI carries no agent-context state at all: it never reads this config, never resolves a context file, and the `__CONTEXT_FILE__` placeholder (if present in any template) is left untouched. All context-file knowledge — including the per-agent default mapping in `agent-context-defaults.json` — lives entirely within this extension, so disabling it is a complete opt-out.
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
# Coding Agent Context Extension Configuration
|
||||
# These values are populated automatically by `specify init` and
|
||||
# `specify integration use` / `specify integration install`.
|
||||
|
||||
# WHAT: The single agent context file relative to the project root (the directory containing .specify/). Absolute paths, backslash separators, and `..` path segments are rejected.
|
||||
# REQUIREMENT: OPTIONAL. Use this if you want to manually specify a single context file. If you leave this entry blank, it will use the default context file for the coding agent you picked when you set up Spec Kit. See `agent-context-defaults.json` for the defaults.
|
||||
# EXAMPLE: context_file: CLAUDE.md
|
||||
# Path (relative to the project root) to the default coding agent context file
|
||||
# managed by this extension (e.g. CLAUDE.md, AGENTS.md,
|
||||
# .github/copilot-instructions.md). Set automatically from the active
|
||||
# integration and regenerated during `specify init` or integration switches.
|
||||
context_file: ""
|
||||
|
||||
# WHAT: List of agent context files relative to the project root (the directory containing .specify/). If you have both `context_file` and `context_files` filled, then this (`context_files`) takes precedence. Absolute paths, backslash separators, and `..` path segments are rejected.
|
||||
# REQUIREMENT: OPTIONAL. Use this if your project requires you to keep multiple agent context files in sync.
|
||||
# EXAMPLE:
|
||||
# context_files:
|
||||
# - AGENTS.md
|
||||
# - CLAUDE.md
|
||||
# Optional list of project-relative coding agent context files managed by this
|
||||
# extension. When non-empty, this list takes precedence over `context_file`.
|
||||
# Use this for projects that intentionally keep multiple agent anchors in sync.
|
||||
context_files: []
|
||||
|
||||
# WHAT: Markers (delimiters) for the managed Spec Kit section. This extension injects information only between these markers.
|
||||
# REQUIREMENT: OPTIONAL. Only change if you wish to have a custom marker name.
|
||||
# EXAMPLE:
|
||||
# context_markers:
|
||||
# start: "<!-- AGENT SPEC KIT CONTEXT START -->"
|
||||
# end: "<!-- AGENT SPEC KIT CONTEXT END -->"
|
||||
# Delimiters for the managed Spec Kit section.
|
||||
# Edit these to use custom markers.
|
||||
context_markers:
|
||||
start: "<!-- SPECKIT START -->"
|
||||
end: "<!-- SPECKIT END -->"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"_comment": "Default coding agent context file per integration, owned by the agent-context extension. Used to self-seed agent-context-config.yml when it declares no context_file/context_files. Keyed by the Spec Kit integration key recorded in .specify/init-options.json. This mapping is independent of the Specify CLI by design.",
|
||||
"agents": {
|
||||
"agy": "AGENTS.md",
|
||||
"alquimia": "ALQUIMIA.md",
|
||||
"amp": "AGENTS.md",
|
||||
"auggie": ".augment/rules/specify-rules.md",
|
||||
"bob": "AGENTS.md",
|
||||
|
||||
@@ -176,18 +176,13 @@ _opts_lines=()
|
||||
while IFS= read -r _line || [[ -n "$_line" ]]; do
|
||||
_opts_lines+=("$_line")
|
||||
done < <(printf '%s\n' "$_raw_opts")
|
||||
if (( ${#_opts_lines[@]} < 1 )); then
|
||||
echo "agent-context: malformed config parser output; expected at least the context_files line, got ${#_opts_lines[@]}; skipping update." >&2
|
||||
if (( ${#_opts_lines[@]} < 3 )); then
|
||||
echo "agent-context: malformed config parser output; expected 3 lines (context_files, marker_start, marker_end), got ${#_opts_lines[@]}; skipping update." >&2
|
||||
exit 0
|
||||
fi
|
||||
# The marker lines may be absent: the $(...) capture above strips trailing
|
||||
# newlines, so blank markers (the config omitting context_markers and relying on
|
||||
# defaults) collapse the 3-line output to fewer lines. Default them to empty here
|
||||
# and let the DEFAULT_START/END substitution below fill them in, matching the
|
||||
# Python and PowerShell ports.
|
||||
CONTEXT_FILES_JSON="${_opts_lines[0]}"
|
||||
MARKER_START="${_opts_lines[1]:-}"
|
||||
MARKER_END="${_opts_lines[2]:-}"
|
||||
MARKER_START="${_opts_lines[1]}"
|
||||
MARKER_END="${_opts_lines[2]}"
|
||||
|
||||
if ! _context_files_raw="$("$_python" - "$CONTEXT_FILES_JSON" <<'PY'
|
||||
import json
|
||||
|
||||
@@ -11,9 +11,8 @@ Usage: update_agent_context.py [plan_path]
|
||||
|
||||
When ``plan_path`` is omitted, the script derives it from
|
||||
``.specify/feature.json`` (written by /speckit-specify). Falls back to the most
|
||||
recently modified ``plan.md`` found anywhere under ``specs/`` — scoped layouts
|
||||
nest it as ``specs/<scope>/<feature>/plan.md`` — only when feature.json is
|
||||
absent or its plan does not exist yet.
|
||||
recently modified ``specs/*/plan.md`` only when feature.json is absent or its
|
||||
plan does not exist yet.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -173,31 +172,16 @@ def _resolve_plan_path(project_root: str) -> str:
|
||||
|
||||
if not plan_path:
|
||||
root = Path(project_root).resolve()
|
||||
specs = root / "specs"
|
||||
|
||||
def _resolved_rel(p: Path) -> Path | None:
|
||||
# Resolve symlinks before checking containment: relative_to() is
|
||||
# lexical and would otherwise accept a plan reached through a specs/
|
||||
# symlink that points outside the project, emitting an
|
||||
# in-project-looking path for an out-of-project file (or picking it
|
||||
# as "most recent").
|
||||
plans = sorted(
|
||||
(root / "specs").glob("*/plan.md"),
|
||||
key=lambda p: p.stat().st_mtime,
|
||||
reverse=True,
|
||||
)
|
||||
if plans:
|
||||
try:
|
||||
return p.resolve().relative_to(root)
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
|
||||
# Recurse (rather than the old one-level specs/*/plan.md glob) so scoped
|
||||
# layouts created via SPECIFY_FEATURE_DIRECTORY, e.g.
|
||||
# specs/<scope>/<feature>/plan.md, are still discovered when
|
||||
# feature.json is absent (#3024). Mirrors the bash and PowerShell twins.
|
||||
candidates = []
|
||||
for p in specs.rglob("plan.md"):
|
||||
rel = _resolved_rel(p)
|
||||
if rel is not None:
|
||||
candidates.append((p, rel))
|
||||
candidates.sort(key=lambda pr: pr[0].stat().st_mtime, reverse=True)
|
||||
if candidates:
|
||||
plan_path = candidates[0][1].as_posix()
|
||||
plan_path = plans[0].relative_to(root).as_posix()
|
||||
except ValueError:
|
||||
plan_path = ""
|
||||
return plan_path
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ Discovery answers *"is this worth building?"* Delivery answers *"how do we build
|
||||
|
||||
## Overview
|
||||
|
||||
`assess` runs inside an initialized Spec Kit project (it writes assessments under `.specify/assessments/`), but that project can be **completely empty of source code** — a freshly initialized project with no code works just as well as an established codebase. The input is just an idea: pasted text, a URL, or a ticket need no existing code, while a codebase pointer lets you assess an idea for code that already exists. Neither starting point is more "correct" than the other.
|
||||
|
||||
Each idea lives in its own directory under `.specify/assessments/<slug>/`, with one Markdown artifact per stage:
|
||||
|
||||
```
|
||||
|
||||
@@ -21,8 +21,6 @@ The user input is the idea and (optionally) a slug. Treat it as one of:
|
||||
3. **A codebase pointer** — phrasing like "an idea for this repo" or a path. Read enough of the repository to record what the idea relates to.
|
||||
4. **A mix** of the above.
|
||||
|
||||
There is **no requirement for existing source code**: within an initialized Spec Kit project, intake works just as well when the project is empty of code as when it already has a codebase. Pasted text or a URL (options 1–2) need no existing codebase; a codebase pointer (option 3) targets existing code. Both are equally valid.
|
||||
|
||||
If the input is empty, ask the user for the idea (interactive), or stop with a note that there is nothing to intake (automated).
|
||||
|
||||
## Slug Resolution
|
||||
|
||||
@@ -1,43 +1,8 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-08-14T00:00:00Z",
|
||||
"updated_at": "2026-07-21T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
|
||||
"extensions": {
|
||||
"adrkit": {
|
||||
"name": "adrkit — decision memory for spec-driven development",
|
||||
"id": "adrkit",
|
||||
"description": "Pulls the decisions governing this work into agent context, checks produced plans against them, and drafts an ADR from a plan artifact.",
|
||||
"author": "Mark Beacom (@mbeacom)",
|
||||
"version": "0.1.2",
|
||||
"download_url": "https://github.com/mbeacom/adrkit/releases/download/spec-kit-v0.1.2/adrkit.zip",
|
||||
"repository": "https://github.com/mbeacom/adrkit",
|
||||
"homepage": "https://adrkit.dev",
|
||||
"documentation": "https://github.com/mbeacom/adrkit/blob/main/packages/adapters/spec-kit/README.md",
|
||||
"changelog": "https://github.com/mbeacom/adrkit/blob/main/CHANGELOG.md",
|
||||
"license": "Apache-2.0",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.13.0,<0.16.0",
|
||||
"tools": [{ "name": "adr", "version": ">=0.3.0", "required": true }]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 3,
|
||||
"hooks": 1
|
||||
},
|
||||
"tags": [
|
||||
"adr",
|
||||
"governance",
|
||||
"decision-records",
|
||||
"architecture",
|
||||
"compliance"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-03T00:00:00Z",
|
||||
"updated_at": "2026-08-03T00:00:00Z"
|
||||
},
|
||||
"aide": {
|
||||
"name": "AI-Driven Engineering (AIDE)",
|
||||
"id": "aide",
|
||||
@@ -286,44 +251,6 @@
|
||||
"created_at": "2026-05-14T00:00:00Z",
|
||||
"updated_at": "2026-06-30T00:00:00Z"
|
||||
},
|
||||
"arch-governance": {
|
||||
"name": "Architecture Governance",
|
||||
"id": "arch-governance",
|
||||
"description": "Keep specs, code & ADRs in sync: citation slots + a read-only, fail-closed validator.",
|
||||
"author": "Ash Brener",
|
||||
"version": "1.2.2",
|
||||
"download_url": "https://github.com/ashbrener/spec-kit-arch-governance/archive/refs/tags/v1.2.2.zip",
|
||||
"repository": "https://github.com/ashbrener/spec-kit-arch-governance",
|
||||
"homepage": "https://github.com/ashbrener/spec-kit-arch-governance",
|
||||
"documentation": "https://github.com/ashbrener/spec-kit-arch-governance/blob/main/README.md",
|
||||
"changelog": "https://github.com/ashbrener/spec-kit-arch-governance/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "docs",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.1.0",
|
||||
"tools": [
|
||||
{ "name": "python", "version": ">=3.11", "required": true },
|
||||
{ "name": "uv", "required": true }
|
||||
]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 6,
|
||||
"hooks": 3
|
||||
},
|
||||
"tags": [
|
||||
"architecture",
|
||||
"governance",
|
||||
"adr",
|
||||
"citations",
|
||||
"spec-sync"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-14T00:00:00Z",
|
||||
"updated_at": "2026-08-14T00:00:00Z"
|
||||
},
|
||||
"architect-preview": {
|
||||
"name": "Architect Impact Previewer",
|
||||
"id": "architect-preview",
|
||||
@@ -363,8 +290,8 @@
|
||||
"id": "architecture-guard",
|
||||
"description": "Framework-agnostic architecture review extension for validating implementation against governance and architecture constitutions, detecting architectural drift, and generating non-blocking refactor tasks.",
|
||||
"author": "DyanGalih",
|
||||
"version": "1.13.1",
|
||||
"download_url": "https://github.com/DyanGalih/spec-kit-architecture-guard/archive/refs/tags/v1.13.1.zip",
|
||||
"version": "1.8.17",
|
||||
"download_url": "https://github.com/DyanGalih/spec-kit-architecture-guard/archive/refs/tags/v1.8.17.zip",
|
||||
"repository": "https://github.com/DyanGalih/spec-kit-architecture-guard",
|
||||
"homepage": "https://github.com/DyanGalih/spec-kit-architecture-guard",
|
||||
"documentation": "https://github.com/DyanGalih/spec-kit-architecture-guard/blob/main/docs/architecture-overview.md",
|
||||
@@ -376,7 +303,7 @@
|
||||
"speckit_version": ">=0.1.0"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 14,
|
||||
"commands": 10,
|
||||
"hooks": 3
|
||||
},
|
||||
"tags": [
|
||||
@@ -386,22 +313,21 @@
|
||||
"refactor",
|
||||
"workflow",
|
||||
"governance",
|
||||
"guardrails",
|
||||
"hygiene"
|
||||
"guardrails"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-05-05T07:26:00Z",
|
||||
"updated_at": "2026-07-24T00:00:00Z"
|
||||
"updated_at": "2026-06-08T00:00:00Z"
|
||||
},
|
||||
"archive": {
|
||||
"name": "Archive Extension",
|
||||
"id": "archive",
|
||||
"description": "Archive merged features into main project memory, resolving gaps and conflicts.",
|
||||
"author": "Stanislav Deviatov",
|
||||
"version": "1.2.2",
|
||||
"download_url": "https://github.com/stn1slv/spec-kit-archive/archive/refs/tags/v1.2.2.zip",
|
||||
"version": "1.0.0",
|
||||
"download_url": "https://github.com/stn1slv/spec-kit-archive/archive/refs/tags/v1.0.0.zip",
|
||||
"repository": "https://github.com/stn1slv/spec-kit-archive",
|
||||
"homepage": "https://github.com/stn1slv/spec-kit-archive",
|
||||
"documentation": "https://github.com/stn1slv/spec-kit-archive/blob/main/README.md",
|
||||
@@ -426,45 +352,7 @@
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-03-14T00:00:00Z",
|
||||
"updated_at": "2026-08-11T00:00:00Z"
|
||||
},
|
||||
"atlas": {
|
||||
"name": "spec-kit-atlas",
|
||||
"id": "atlas",
|
||||
"description": "Synthesize spec-kit specs into faithful, interactive architecture storybooks & doc portals.",
|
||||
"author": "Ash Brener",
|
||||
"version": "0.1.0",
|
||||
"download_url": "https://github.com/ashbrener/spec-kit-atlas/archive/refs/tags/v0.1.0.zip",
|
||||
"repository": "https://github.com/ashbrener/spec-kit-atlas",
|
||||
"homepage": "https://github.com/ashbrener/spec-kit-atlas",
|
||||
"documentation": "https://github.com/ashbrener/spec-kit-atlas/blob/main/README.md",
|
||||
"changelog": "https://github.com/ashbrener/spec-kit-atlas/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "docs",
|
||||
"effect": "read-only",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.1.0",
|
||||
"tools": [
|
||||
{ "name": "python", "version": ">=3.11", "required": true },
|
||||
{ "name": "uv", "required": true }
|
||||
]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 2,
|
||||
"hooks": 0
|
||||
},
|
||||
"tags": [
|
||||
"documentation",
|
||||
"architecture",
|
||||
"storybook",
|
||||
"traceability",
|
||||
"atlas"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-13T00:00:00Z",
|
||||
"updated_at": "2026-08-13T00:00:00Z"
|
||||
"updated_at": "2026-03-14T00:00:00Z"
|
||||
},
|
||||
"azure-devops": {
|
||||
"name": "Azure DevOps Integration",
|
||||
@@ -601,46 +489,6 @@
|
||||
"created_at": "2026-04-17T00:00:00Z",
|
||||
"updated_at": "2026-04-17T00:00:00Z"
|
||||
},
|
||||
"blueprint-index": {
|
||||
"name": "Blueprint Index — Living Architecture Map",
|
||||
"id": "blueprint-index",
|
||||
"description": "Living architecture map for brownfield and greenfield projects, with a deterministic CI gate that blocks contradictions between the map, specs, and code while warning on non-blocking drift.",
|
||||
"author": "ogil109",
|
||||
"version": "0.2.0",
|
||||
"download_url": "https://github.com/ogil109/spec-kit-blueprint/releases/download/v0.2.0/blueprint.zip",
|
||||
"repository": "https://github.com/ogil109/spec-kit-blueprint",
|
||||
"homepage": "https://github.com/ogil109/spec-kit-blueprint/tree/main",
|
||||
"documentation": "https://github.com/ogil109/spec-kit-blueprint/blob/main/README.md",
|
||||
"changelog": "https://github.com/ogil109/spec-kit-blueprint/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.10.0",
|
||||
"tools": [
|
||||
{ "name": "bash", "required": false },
|
||||
{ "name": "git", "required": false }
|
||||
]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 4,
|
||||
"hooks": 0
|
||||
},
|
||||
"tags": [
|
||||
"blueprint",
|
||||
"architecture",
|
||||
"coherence",
|
||||
"drift",
|
||||
"brownfield",
|
||||
"autonomous",
|
||||
"ci"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-24T00:00:00Z",
|
||||
"updated_at": "2026-07-24T00:00:00Z"
|
||||
},
|
||||
"branch-convention": {
|
||||
"name": "Branch Convention",
|
||||
"id": "branch-convention",
|
||||
@@ -887,8 +735,8 @@
|
||||
"id": "charter",
|
||||
"description": "Compose modular project constitutions from shared fragment registries. Centralize governance rules, select per-project fragments, track upstream changes, and keep multi-project setups consistent.",
|
||||
"author": "Fyloss",
|
||||
"version": "0.5.1",
|
||||
"download_url": "https://github.com/Fyloss/spec-kit-charter/archive/refs/tags/v0.5.1.zip",
|
||||
"version": "0.3.1",
|
||||
"download_url": "https://github.com/Fyloss/spec-kit-charter/archive/refs/tags/v0.3.1.zip",
|
||||
"repository": "https://github.com/Fyloss/spec-kit-charter",
|
||||
"homepage": "https://github.com/Fyloss/spec-kit-charter",
|
||||
"documentation": "https://github.com/Fyloss/spec-kit-charter/tree/master/docs",
|
||||
@@ -897,8 +745,7 @@
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.11.9",
|
||||
"tools": [{ "name": "git", "required": false }]
|
||||
"speckit_version": ">=0.11.9"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 5,
|
||||
@@ -915,7 +762,7 @@
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-06T00:00:00Z",
|
||||
"updated_at": "2026-08-04T00:00:00Z"
|
||||
"updated_at": "2026-07-06T00:00:00Z"
|
||||
},
|
||||
"ci-guard": {
|
||||
"name": "CI Guard",
|
||||
@@ -1182,41 +1029,6 @@
|
||||
"created_at": "2026-03-29T00:00:00Z",
|
||||
"updated_at": "2026-03-29T00:00:00Z"
|
||||
},
|
||||
"contextforge-mcp": {
|
||||
"name": "ContextForge MCP",
|
||||
"id": "contextforge-mcp",
|
||||
"description": "Integrates codebase-memory-mcp + headroom into Spec Kit — graph-based code intelligence and context compression for the implement phase.",
|
||||
"author": "capatinore",
|
||||
"version": "0.1.0",
|
||||
"download_url": "https://github.com/capatinore/contextforge-mcp/releases/download/ext-v0.1.0/contextforge-mcp-speckit-extension.zip",
|
||||
"repository": "https://github.com/capatinore/contextforge-mcp",
|
||||
"homepage": "https://github.com/capatinore/contextforge-mcp",
|
||||
"documentation": "https://github.com/capatinore/contextforge-mcp/blob/main/README.md",
|
||||
"changelog": "",
|
||||
"license": "MIT",
|
||||
"category": "code",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.10.0"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 4,
|
||||
"hooks": 0
|
||||
},
|
||||
"tags": [
|
||||
"mcp",
|
||||
"code-intelligence",
|
||||
"context-compression",
|
||||
"tokens",
|
||||
"claude",
|
||||
"spec-driven-development"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-13T00:00:00Z",
|
||||
"updated_at": "2026-07-13T00:00:00Z"
|
||||
},
|
||||
"cost": {
|
||||
"name": "Cost Tracker",
|
||||
"id": "cost",
|
||||
@@ -1766,8 +1578,8 @@
|
||||
"id": "gates",
|
||||
"description": "Deterministic quality enforcement for Spec Kit across agent hooks, git checks, and CI pipelines with one policy file and one verify entrypoint for identical results at every boundary.",
|
||||
"author": "schwichtgit",
|
||||
"version": "0.3.3",
|
||||
"download_url": "https://github.com/schwichtgit/spec-gates/releases/download/v0.3.3/gates-0.3.3.zip",
|
||||
"version": "0.3.2",
|
||||
"download_url": "https://github.com/schwichtgit/spec-gates/releases/download/v0.3.2/gates-0.3.2.zip",
|
||||
"repository": "https://github.com/schwichtgit/spec-gates",
|
||||
"homepage": "https://github.com/schwichtgit/spec-gates",
|
||||
"documentation": "https://github.com/schwichtgit/spec-gates/blob/main/docs/how-it-works.md",
|
||||
@@ -1811,7 +1623,7 @@
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-09T00:00:00Z",
|
||||
"updated_at": "2026-07-27T00:00:00Z"
|
||||
"updated_at": "2026-07-15T00:00:00Z"
|
||||
},
|
||||
"github-issues": {
|
||||
"name": "GitHub Issues Integration 1",
|
||||
@@ -1891,41 +1703,6 @@
|
||||
"created_at": "2026-06-24T00:00:00Z",
|
||||
"updated_at": "2026-07-07T00:00:00Z"
|
||||
},
|
||||
"grill": {
|
||||
"name": "SpecKit Grill Me",
|
||||
"id": "grill",
|
||||
"description": "Exhaustively resolve specification ambiguities and decisions before planning.",
|
||||
"author": "yoshi1220",
|
||||
"version": "1.0.0",
|
||||
"download_url": "https://github.com/yoshi1220/speckit-grill-me/releases/download/v1.0.0/speckit-grill-me-extension-v1.0.0.zip",
|
||||
"repository": "https://github.com/yoshi1220/speckit-grill-me",
|
||||
"homepage": "https://github.com/yoshi1220/speckit-grill-me/tree/main/spec-kit-extension",
|
||||
"documentation": "https://github.com/yoshi1220/speckit-grill-me/blob/main/spec-kit-extension/README.md",
|
||||
"changelog": "https://github.com/yoshi1220/speckit-grill-me/blob/main/spec-kit-extension/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.16.2",
|
||||
"tools": [{ "name": "bash", "required": true }]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 1,
|
||||
"hooks": 0
|
||||
},
|
||||
"tags": [
|
||||
"clarification",
|
||||
"requirements",
|
||||
"specification",
|
||||
"elicitation",
|
||||
"workflow"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-11T00:00:00Z",
|
||||
"updated_at": "2026-08-11T00:00:00Z"
|
||||
},
|
||||
"harness": {
|
||||
"name": "Research Harness",
|
||||
"id": "harness",
|
||||
@@ -2043,40 +1820,6 @@
|
||||
"created_at": "2026-06-23T00:00:00Z",
|
||||
"updated_at": "2026-06-30T00:00:00Z"
|
||||
},
|
||||
"intent": {
|
||||
"name": "Intent Reconciliation",
|
||||
"id": "intent",
|
||||
"description": "Reconcile implementation-discovered decisions against approved feature intent",
|
||||
"author": "SuhaibAslam",
|
||||
"version": "1.0.2",
|
||||
"download_url": "https://github.com/SuhaibAslam/spec-kit-reconcile/archive/refs/tags/v1.0.2.zip",
|
||||
"repository": "https://github.com/SuhaibAslam/spec-kit-reconcile",
|
||||
"homepage": "https://github.com/SuhaibAslam/spec-kit-reconcile",
|
||||
"documentation": "https://github.com/SuhaibAslam/spec-kit-reconcile/blob/main/README.md",
|
||||
"changelog": "https://github.com/SuhaibAslam/spec-kit-reconcile/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.12.0"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 3,
|
||||
"hooks": 0
|
||||
},
|
||||
"tags": [
|
||||
"intent",
|
||||
"decisions",
|
||||
"reconciliation",
|
||||
"drift",
|
||||
"workflow"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-29T00:00:00Z",
|
||||
"updated_at": "2026-07-29T00:00:00Z"
|
||||
},
|
||||
"issue": {
|
||||
"name": "GitHub Issues Integration 2",
|
||||
"id": "issue",
|
||||
@@ -2218,40 +1961,6 @@
|
||||
"created_at": "2026-06-08T00:00:00Z",
|
||||
"updated_at": "2026-06-24T00:00:00Z"
|
||||
},
|
||||
"keel": {
|
||||
"name": "Keel Discovery",
|
||||
"id": "keel",
|
||||
"description": "A Spec Kit extension that puts customer evidence upstream of /speckit.specify, and audits what you shipped against it afterwards.",
|
||||
"author": "Keel Discovery",
|
||||
"version": "0.1.1",
|
||||
"download_url": "https://github.com/keeldiscovery/spec-kit-keel/archive/refs/tags/v0.1.1.zip",
|
||||
"repository": "https://github.com/keeldiscovery/spec-kit-keel",
|
||||
"homepage": "https://keeldiscovery.com",
|
||||
"documentation": "https://github.com/keeldiscovery/spec-kit-keel/blob/main/README.md",
|
||||
"changelog": "https://github.com/keeldiscovery/spec-kit-keel/blob/main/CHANGELOG.md",
|
||||
"license": "Apache-2.0",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.15.0"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 5,
|
||||
"hooks": 2
|
||||
},
|
||||
"tags": [
|
||||
"discovery",
|
||||
"evidence",
|
||||
"customer-research",
|
||||
"validation",
|
||||
"traceability"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-10T00:00:00Z",
|
||||
"updated_at": "2026-08-10T00:00:00Z"
|
||||
},
|
||||
"learn": {
|
||||
"name": "Learning Extension",
|
||||
"id": "learn",
|
||||
@@ -2320,40 +2029,6 @@
|
||||
"created_at": "2026-06-01T00:00:00Z",
|
||||
"updated_at": "2026-06-22T00:00:00Z"
|
||||
},
|
||||
"linear-weave": {
|
||||
"name": "Linear Weave",
|
||||
"id": "linear-weave",
|
||||
"description": "Weave Spec Kit into Linear: pull requirements, mirror tasks.md into sub-issues, sync statuses.",
|
||||
"author": "Tony Woodhouse",
|
||||
"version": "1.0.1",
|
||||
"download_url": "https://github.com/tonydwoodhouse/spec-kit-linear-weave/archive/refs/tags/v1.0.1.zip",
|
||||
"repository": "https://github.com/tonydwoodhouse/spec-kit-linear-weave",
|
||||
"homepage": "https://github.com/tonydwoodhouse/spec-kit-linear-weave",
|
||||
"documentation": "https://github.com/tonydwoodhouse/spec-kit-linear-weave/blob/main/README.md",
|
||||
"changelog": "https://github.com/tonydwoodhouse/spec-kit-linear-weave/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "integration",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.13.0,<1.0.0",
|
||||
"tools": [{ "name": "linear-mcp", "required": true }]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 5,
|
||||
"hooks": 5
|
||||
},
|
||||
"tags": [
|
||||
"linear",
|
||||
"issue-tracking",
|
||||
"integration",
|
||||
"workflow"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-21T00:00:00Z",
|
||||
"updated_at": "2026-07-27T00:00:00Z"
|
||||
},
|
||||
"loop": {
|
||||
"name": "Loop Engineering",
|
||||
"id": "loop",
|
||||
@@ -3551,8 +3226,8 @@
|
||||
"id": "reconcile",
|
||||
"description": "Reconcile implementation drift by surgically updating the feature's own spec, plan, and tasks.",
|
||||
"author": "Stanislav Deviatov",
|
||||
"version": "1.1.0",
|
||||
"download_url": "https://github.com/stn1slv/spec-kit-reconcile/archive/refs/tags/v1.1.0.zip",
|
||||
"version": "1.0.0",
|
||||
"download_url": "https://github.com/stn1slv/spec-kit-reconcile/archive/refs/tags/v1.0.0.zip",
|
||||
"repository": "https://github.com/stn1slv/spec-kit-reconcile",
|
||||
"homepage": "https://github.com/stn1slv/spec-kit-reconcile",
|
||||
"documentation": "https://github.com/stn1slv/spec-kit-reconcile/blob/main/README.md",
|
||||
@@ -3577,7 +3252,7 @@
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-03-14T00:00:00Z",
|
||||
"updated_at": "2026-08-10T00:00:00Z"
|
||||
"updated_at": "2026-03-14T00:00:00Z"
|
||||
},
|
||||
"red-team": {
|
||||
"name": "Red Team",
|
||||
@@ -4224,78 +3899,6 @@
|
||||
"created_at": "2026-04-30T00:00:00Z",
|
||||
"updated_at": "2026-04-30T00:00:00Z"
|
||||
},
|
||||
"specassay-check": {
|
||||
"name": "SpecAssay Check",
|
||||
"id": "specassay-check",
|
||||
"description": "Gate 2 refuses silent gaps and emits a trace-manifest (trace-manifest.json).",
|
||||
"author": "Rik Dryfoos",
|
||||
"version": "0.3.3",
|
||||
"download_url": "https://github.com/rdryfoos/specassay/releases/download/v0.3.3/specassay-check-0.3.3.zip",
|
||||
"repository": "https://github.com/rdryfoos/specassay",
|
||||
"homepage": "https://www.specassay.com",
|
||||
"documentation": "https://github.com/rdryfoos/specassay/blob/main/extensions/specassay-check/README.md",
|
||||
"changelog": "https://github.com/rdryfoos/specassay/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "visibility",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.14.0",
|
||||
"tools": [
|
||||
{ "name": "bash", "required": true },
|
||||
{ "name": "python3", "version": ">=3.8", "required": true }
|
||||
]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 1,
|
||||
"hooks": 1
|
||||
},
|
||||
"tags": [
|
||||
"traceability",
|
||||
"gate",
|
||||
"ci",
|
||||
"governance",
|
||||
"sdd"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-13T00:00:00Z",
|
||||
"updated_at": "2026-08-13T00:00:00Z"
|
||||
},
|
||||
"specjudge": {
|
||||
"name": "SpecJudge — right-size the model before you implement",
|
||||
"id": "specjudge",
|
||||
"description": "Recommends the model that fits your tasks, citing the spec fragment behind every level.",
|
||||
"author": "Joaquín Ruiz",
|
||||
"version": "0.5.4",
|
||||
"download_url": "https://github.com/JoaquinRuiz/SpecJudge/releases/download/v0.5.4/spec-kit-specjudge.zip",
|
||||
"repository": "https://github.com/JoaquinRuiz/SpecJudge",
|
||||
"homepage": "https://github.com/JoaquinRuiz/SpecJudge",
|
||||
"documentation": "https://github.com/JoaquinRuiz/SpecJudge/blob/main/extensions/spec-kit/README.md",
|
||||
"changelog": "https://github.com/JoaquinRuiz/SpecJudge/blob/main/extensions/spec-kit/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-only",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.13.0",
|
||||
"tools": [{ "name": "specjudge", "version": ">=0.5.0", "required": true }]
|
||||
},
|
||||
"provides": {
|
||||
"commands": 1,
|
||||
"hooks": 1
|
||||
},
|
||||
"tags": [
|
||||
"cost",
|
||||
"model-selection",
|
||||
"local-first",
|
||||
"ollama"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-12T00:00:00Z",
|
||||
"updated_at": "2026-08-12T00:00:00Z"
|
||||
},
|
||||
"speckit-superpowers-bridge": {
|
||||
"name": "Superpowers Implementation Bridge",
|
||||
"id": "speckit-superpowers-bridge",
|
||||
@@ -4604,8 +4207,8 @@
|
||||
"id": "superspec",
|
||||
"description": "Bridges spec-kit workflows with obra/superpowers capabilities for brainstorming, TDD, code review, and resumable execution.",
|
||||
"author": "WangX0111",
|
||||
"version": "1.0.2",
|
||||
"download_url": "https://github.com/WangX0111/superspec/archive/refs/tags/v1.0.2.zip",
|
||||
"version": "1.0.1",
|
||||
"download_url": "https://github.com/WangX0111/superspec/archive/refs/tags/v1.0.1.zip",
|
||||
"repository": "https://github.com/WangX0111/superspec",
|
||||
"homepage": "https://github.com/WangX0111/superspec",
|
||||
"documentation": "https://github.com/WangX0111/superspec/blob/main/README.md",
|
||||
@@ -4632,7 +4235,7 @@
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-04-22T00:00:00Z",
|
||||
"updated_at": "2026-08-14T00:00:00Z"
|
||||
"updated_at": "2026-05-30T00:00:00Z"
|
||||
},
|
||||
"sync": {
|
||||
"name": "Spec Sync",
|
||||
@@ -4706,47 +4309,6 @@
|
||||
"created_at": "2026-06-22T00:00:00Z",
|
||||
"updated_at": "2026-06-22T00:00:00Z"
|
||||
},
|
||||
"tdd": {
|
||||
"name": "TDD Extension",
|
||||
"id": "tdd",
|
||||
"description": "Drives spec-kit implementation with tests: a language-agnostic red-green-refactor loop with a per-feature test list, recorded red and green evidence, and mutation-checked test strength.",
|
||||
"author": "d0whc3r",
|
||||
"version": "1.1.2",
|
||||
"download_url": "https://github.com/d0whc3r/spec-kit-tdd/releases/download/v1.1.2/tdd-1.1.2.zip",
|
||||
"repository": "https://github.com/d0whc3r/spec-kit-tdd",
|
||||
"homepage": "https://d0whc3r.github.io/spec-kit-tdd/",
|
||||
"documentation": "https://github.com/d0whc3r/spec-kit-tdd/wiki",
|
||||
"changelog": "https://github.com/d0whc3r/spec-kit-tdd/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.11.9"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 4,
|
||||
"hooks": 3
|
||||
},
|
||||
"tags": [
|
||||
"acceptance-tests",
|
||||
"mutation-testing",
|
||||
"property-based-testing",
|
||||
"quality",
|
||||
"red-green-refactor",
|
||||
"spec-kit",
|
||||
"spec-kit-extension",
|
||||
"tdd",
|
||||
"test-driven-development",
|
||||
"test-first",
|
||||
"testing",
|
||||
"unit-tests"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-08-04T00:00:00Z",
|
||||
"updated_at": "2026-08-04T00:00:00Z"
|
||||
},
|
||||
"team-assign": {
|
||||
"name": "Team Assign",
|
||||
"id": "team-assign",
|
||||
@@ -5181,40 +4743,36 @@
|
||||
"verify-review-ship": {
|
||||
"name": "Verify Review Ship",
|
||||
"id": "verify-review-ship",
|
||||
"description": "Post-convergence operational verification, technical review, learning governance, and transactional delivery.",
|
||||
"description": "Adds post-implementation verify, review, and ship readiness gates to Spec Kit workflows.",
|
||||
"author": "Carlos Eduardo Gevaerd Araujo",
|
||||
"version": "0.4.2",
|
||||
"download_url": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/archive/refs/tags/v0.4.2.zip",
|
||||
"sha256": "71dceef5bf81d7ac54faa26bb5cf279554815a4928ee8d0c8e9bfb4c3e2bb0ab",
|
||||
"version": "0.1.0",
|
||||
"download_url": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/archive/refs/tags/v0.1.0.zip",
|
||||
"repository": "https://github.com/cadugevaerd/spec-kit-verify-review-ship",
|
||||
"homepage": "https://github.com/cadugevaerd/spec-kit-verify-review-ship",
|
||||
"documentation": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/blob/main/README.md",
|
||||
"changelog": "https://github.com/cadugevaerd/spec-kit-verify-review-ship/blob/main/CHANGELOG.md",
|
||||
"license": "MIT",
|
||||
"category": "process",
|
||||
"effect": "read-write",
|
||||
"effect": "read-only",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.11.2"
|
||||
"speckit_version": ">=0.1.0"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 3,
|
||||
"hooks": 0
|
||||
"hooks": 1
|
||||
},
|
||||
"tags": [
|
||||
"quality",
|
||||
"review",
|
||||
"shipping",
|
||||
"merge",
|
||||
"cleanup",
|
||||
"learning",
|
||||
"governance",
|
||||
"agent-skills"
|
||||
"workflow",
|
||||
"testing"
|
||||
],
|
||||
"verified": false,
|
||||
"downloads": 0,
|
||||
"stars": 0,
|
||||
"created_at": "2026-07-10T00:00:00Z",
|
||||
"updated_at": "2026-07-28T00:00:00Z"
|
||||
"updated_at": "2026-07-10T00:00:00Z"
|
||||
},
|
||||
"verify-tasks": {
|
||||
"name": "Verify Tasks Extension",
|
||||
|
||||
@@ -10,7 +10,7 @@ This extension provides Git operations as an optional, self-contained module. It
|
||||
- **Feature branch creation** with sequential (`001-feature-name`) or timestamp (`20260319-143022-feature-name`) numbering and optional templates for branch namespaces
|
||||
- **Branch validation** to ensure branches follow naming conventions
|
||||
- **Git remote detection** for GitHub integration (e.g., issue creation)
|
||||
- **Auto-commit** after core commands (configurable per-command with custom messages, or Conventional Commit messages generated by the agent)
|
||||
- **Auto-commit** after core commands (configurable per-command with custom messages)
|
||||
|
||||
## Commands
|
||||
|
||||
@@ -66,11 +66,6 @@ branch_prefix: ""
|
||||
# Custom commit message for git init
|
||||
init_commit_message: "[Spec Kit] Initial commit"
|
||||
|
||||
# Commit message style for auto-commit hooks: "fixed" (default) uses the
|
||||
# messages below; "conventional" asks the agent to generate a Conventional
|
||||
# Commit message (e.g. "feat: add OAuth spec") from the diff instead.
|
||||
commit_style: fixed
|
||||
|
||||
# Auto-commit per command (all disabled by default)
|
||||
# Example: enable auto-commit after specify
|
||||
auto_commit:
|
||||
|
||||
@@ -14,37 +14,23 @@ This command is invoked as a hook after (or before) core commands. It:
|
||||
2. Checks `.specify/extensions/git/git-config.yml` for the `auto_commit` section
|
||||
3. Looks up the specific event key to see if auto-commit is enabled
|
||||
4. Falls back to `auto_commit.default` if no event-specific key exists
|
||||
5. Determines the commit message based on `commit_style` (see below)
|
||||
5. Uses the per-command `message` if configured, otherwise a default message
|
||||
6. If enabled and there are uncommitted changes, runs `git add .` + `git commit`
|
||||
|
||||
## Commit Message Styles
|
||||
|
||||
Controlled by the `commit_style` key in `.specify/extensions/git/git-config.yml`:
|
||||
|
||||
- **`fixed`** (default): use the per-command `message` if configured, otherwise a generic `[Spec Kit] Auto-commit <phase> <command>` message.
|
||||
- **`conventional`**: inspect the actual changes (`git diff` / `git status`) since the last commit and generate a single-line [Conventional Commit](https://www.conventionalcommits.org/) message (`type(scope): subject`, e.g. `feat: add OAuth specification` or `docs: update implementation plan`) that accurately summarizes the change. Write this message to a temporary file and pass the file's path to the script (see Execution below). The configured `message` values are ignored in this mode.
|
||||
|
||||
## Execution
|
||||
|
||||
Determine the event name from the hook that triggered this command, then run the script:
|
||||
|
||||
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name> [--message-file <path>]`
|
||||
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name> [-MessageFile <path>]`
|
||||
- **Bash**: `.specify/extensions/git/scripts/bash/auto-commit.sh <event_name>`
|
||||
- **PowerShell**: `.specify/extensions/git/scripts/powershell/auto-commit.ps1 <event_name>`
|
||||
|
||||
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`). Only pass a generated message when `commit_style: conventional` is configured — first check `.specify/extensions/git/git-config.yml` for the value of `commit_style`:
|
||||
|
||||
- If `conventional`: inspect the diff and generate a Conventional Commit message. **Do not interpolate the generated message directly into a shell command string** — its content is derived from repository changes and may contain characters (quotes, `$(...)`, backticks) that a shell would execute or that would break command quoting. Instead, write the message to a temporary file using your file-editing tool (not a shell `echo`/`printf`), then pass that file's path via `--message-file <path>` (Bash) or `-MessageFile <path>` (PowerShell).
|
||||
- If `fixed` or absent: run the script with just `<event_name>`; it uses the configured/static message.
|
||||
Replace `<event_name>` with the actual hook event (e.g., `after_specify`, `before_plan`, `after_implement`).
|
||||
|
||||
## Configuration
|
||||
|
||||
In `.specify/extensions/git/git-config.yml`:
|
||||
|
||||
```yaml
|
||||
# "fixed" (default) uses the messages below; "conventional" asks the agent
|
||||
# to generate a Conventional Commit message from the diff instead.
|
||||
commit_style: fixed
|
||||
|
||||
auto_commit:
|
||||
default: false # Global toggle — set true to enable for all commands
|
||||
after_specify:
|
||||
@@ -60,4 +46,3 @@ auto_commit:
|
||||
- If Git is not available or the current directory is not a repository: skips with a warning
|
||||
- If no config file exists: skips (disabled by default)
|
||||
- If no changes to commit: skips with a message
|
||||
- If `commit_style: conventional` is set and no generated message was supplied: fails with a clear error instead of silently falling back to the fixed message format
|
||||
|
||||
@@ -17,13 +17,6 @@ branch_prefix: ""
|
||||
# Commit message used by `git commit` during repository initialization
|
||||
init_commit_message: "[Spec Kit] Initial commit"
|
||||
|
||||
# Commit message style used by auto-commit hooks (speckit.git.commit):
|
||||
# "fixed" - default; use the configured/static messages below.
|
||||
# "conventional" - ask the agent to inspect the diff and generate a
|
||||
# Conventional Commit message (e.g. "feat: add OAuth spec")
|
||||
# instead of using the messages configured below.
|
||||
commit_style: fixed
|
||||
|
||||
# Auto-commit before/after core commands.
|
||||
# Set "default" to enable for all commands, then override per-command.
|
||||
# Each key can be true/false. Message is customizable per-command.
|
||||
|
||||
@@ -17,13 +17,6 @@ branch_prefix: ""
|
||||
# Commit message used by `git commit` during repository initialization
|
||||
init_commit_message: "[Spec Kit] Initial commit"
|
||||
|
||||
# Commit message style used by auto-commit hooks (speckit.git.commit):
|
||||
# "fixed" - default; use the configured/static messages below.
|
||||
# "conventional" - ask the agent to inspect the diff and generate a
|
||||
# Conventional Commit message (e.g. "feat: add OAuth spec")
|
||||
# instead of using the messages configured below.
|
||||
commit_style: fixed
|
||||
|
||||
# Auto-commit before/after core commands.
|
||||
# Set "default" to enable for all commands, then override per-command.
|
||||
# Each key can be true/false. Message is customizable per-command.
|
||||
|
||||
@@ -3,57 +3,16 @@
|
||||
# Automatically commit changes after a Spec Kit command completes.
|
||||
# Checks per-command config keys in git-config.yml before committing.
|
||||
#
|
||||
# Usage: auto-commit.sh <event_name> [generated_message]
|
||||
# auto-commit.sh <event_name> --message-file <path>
|
||||
# Usage: auto-commit.sh <event_name>
|
||||
# e.g.: auto-commit.sh after_specify
|
||||
# e.g.: auto-commit.sh after_specify --message-file /tmp/commit-msg.txt (commit_style: conventional)
|
||||
#
|
||||
# --message-file is the preferred way to supply an agent-generated commit
|
||||
# message: it reads the message from a file instead of a shell argument,
|
||||
# so message content (which may contain quotes, `$(...)`, backticks, etc.)
|
||||
# is never interpolated into a shell command line.
|
||||
|
||||
set -e
|
||||
|
||||
EVENT_NAME="${1:-}"
|
||||
if [ -z "$EVENT_NAME" ]; then
|
||||
echo "Usage: $0 <event_name> [generated_message | --message-file <path>]" >&2
|
||||
echo "Usage: $0 <event_name>" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift || true
|
||||
|
||||
# Optional second argument: an agent-generated commit message (used when
|
||||
# commit_style: conventional is configured). Prefer --message-file over
|
||||
# passing the message directly as a shell argument.
|
||||
GENERATED_MESSAGE=""
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--message-file)
|
||||
_message_file="${2:-}"
|
||||
if [ -z "$_message_file" ]; then
|
||||
echo "[specify] Error: --message-file requires a path argument" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$_message_file" ]; then
|
||||
echo "[specify] Error: message file '$_message_file' not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
GENERATED_MESSAGE="$(cat "$_message_file")"
|
||||
# The message file is a transport-only artifact: its content is
|
||||
# now captured above, so remove it immediately. Otherwise, if it
|
||||
# was written inside the worktree, it would be picked up as an
|
||||
# untracked change by both the "any changes?" check below and by
|
||||
# `git add .`, polluting the commit or defeating the no-changes
|
||||
# short-circuit even when nothing else changed.
|
||||
rm -f "$_message_file"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
GENERATED_MESSAGE="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
@@ -87,22 +46,8 @@ fi
|
||||
_config_file="$REPO_ROOT/.specify/extensions/git/git-config.yml"
|
||||
_enabled=false
|
||||
_commit_msg=""
|
||||
_commit_style="fixed"
|
||||
|
||||
if [ -f "$_config_file" ]; then
|
||||
# Top-level scalar key: commit_style (fixed | conventional)
|
||||
_style_val=$(grep -m1 '^commit_style:' "$_config_file" 2>/dev/null | sed 's/^commit_style:[[:space:]]*//' | sed 's/[[:space:]]\{1,\}#.*$//' | sed 's/[[:space:]]*$//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//' | tr '[:upper:]' '[:lower:]')
|
||||
if [ -n "$_style_val" ]; then
|
||||
case "$_style_val" in
|
||||
fixed|conventional)
|
||||
_commit_style="$_style_val"
|
||||
;;
|
||||
*)
|
||||
echo "[specify] Warning: unknown commit_style '$_style_val' in git-config.yml (expected 'fixed' or 'conventional'); defaulting to 'fixed'" >&2
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Parse the auto_commit section for this event.
|
||||
# Look for auto_commit.<event_name>.enabled and .message
|
||||
# Also check auto_commit.default as fallback.
|
||||
@@ -149,12 +94,7 @@ if [ -f "$_config_file" ]; then
|
||||
[ "$_val" = "false" ] && _enabled=false
|
||||
fi
|
||||
if echo "$_line" | grep -Eq '[[:space:]]+message:'; then
|
||||
# Trim trailing whitespace before stripping the closing quote:
|
||||
# a value like `message: "Done" ` (trailing spaces after the
|
||||
# quote) would otherwise leave the quote dangling (`Done" `),
|
||||
# since the closing-quote strip is anchored to end-of-string.
|
||||
# The PowerShell twin .Trim()s first; match it for parity.
|
||||
_commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/[[:space:]]*$//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
|
||||
_commit_msg=$(echo "$_line" | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'\'']//' | sed 's/["'\'']*$//')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -183,17 +123,6 @@ if git diff --quiet HEAD 2>/dev/null && git diff --cached --quiet 2>/dev/null &&
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# In conventional mode, the commit message must be supplied by the agent
|
||||
# (via the generated_message argument); never fall back to the fixed message.
|
||||
if [ "$_commit_style" = "conventional" ]; then
|
||||
if [ -n "$GENERATED_MESSAGE" ]; then
|
||||
_commit_msg="$GENERATED_MESSAGE"
|
||||
else
|
||||
echo "[specify] Error: commit_style is 'conventional' but no generated commit message was supplied; aborting auto-commit (pass --message-file <path>, or a raw message as arg 2, or set commit_style: fixed)" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Derive a human-readable command name from the event
|
||||
# e.g., after_specify -> specify, before_plan -> plan
|
||||
_command_name=$(echo "$EVENT_NAME" | sed 's/^after_//' | sed 's/^before_//')
|
||||
|
||||
@@ -3,47 +3,14 @@
|
||||
# Automatically commit changes after a Spec Kit command completes.
|
||||
# Checks per-command config keys in git-config.yml before committing.
|
||||
#
|
||||
# Usage: auto-commit.ps1 <event_name> [generated_message]
|
||||
# auto-commit.ps1 <event_name> -MessageFile <path>
|
||||
# Usage: auto-commit.ps1 <event_name>
|
||||
# e.g.: auto-commit.ps1 after_specify
|
||||
# e.g.: auto-commit.ps1 after_specify -MessageFile C:\temp\commit-msg.txt (commit_style: conventional)
|
||||
#
|
||||
# -MessageFile is the preferred way to supply an agent-generated commit
|
||||
# message: it reads the message from a file instead of a shell argument,
|
||||
# so message content (which may contain quotes, $(...), backticks, etc.)
|
||||
# is never interpolated into a shell command line.
|
||||
param(
|
||||
[Parameter(Position = 0, Mandatory = $true)]
|
||||
[string]$EventName,
|
||||
|
||||
# Optional agent-generated commit message (used when commit_style: conventional is configured).
|
||||
# Prefer -MessageFile over passing the message directly as a shell argument.
|
||||
[Parameter(Position = 1, Mandatory = $false)]
|
||||
[string]$GeneratedMessage = "",
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$MessageFile = ""
|
||||
[string]$EventName
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if ($MessageFile) {
|
||||
if (-not (Test-Path $MessageFile -PathType Leaf)) {
|
||||
Write-Warning "[specify] Error: message file '$MessageFile' not found"
|
||||
exit 1
|
||||
}
|
||||
$GeneratedMessage = (Get-Content -Path $MessageFile -Raw)
|
||||
if ($null -ne $GeneratedMessage) {
|
||||
$GeneratedMessage = $GeneratedMessage.TrimEnd("`r", "`n")
|
||||
}
|
||||
# The message file is a transport-only artifact: its content is now
|
||||
# captured above, so remove it immediately. Otherwise, if it was written
|
||||
# inside the worktree, it would be picked up as an untracked change by
|
||||
# both the "any changes?" check below and by `git add .`, polluting the
|
||||
# commit or defeating the no-changes short-circuit even when nothing
|
||||
# else changed.
|
||||
Remove-Item -Path $MessageFile -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
function Find-ProjectRoot {
|
||||
param([string]$StartDir)
|
||||
$current = Resolve-Path $StartDir
|
||||
@@ -88,25 +55,8 @@ if (-not $isRepo) {
|
||||
$configFile = Join-Path $repoRoot ".specify/extensions/git/git-config.yml"
|
||||
$enabled = $false
|
||||
$commitMsg = ""
|
||||
$commitStyle = "fixed"
|
||||
|
||||
if (Test-Path $configFile) {
|
||||
# Top-level scalar key: commit_style (fixed | conventional)
|
||||
foreach ($line in Get-Content $configFile) {
|
||||
if ($line -match '^commit_style:\s*(.+)$') {
|
||||
$styleVal = (($matches[1] -replace '\s+#.*$', '').Trim()) -replace '^["'']' -replace '["'']$'
|
||||
if ($styleVal) {
|
||||
$styleVal = $styleVal.ToLower()
|
||||
if ($styleVal -eq 'fixed' -or $styleVal -eq 'conventional') {
|
||||
$commitStyle = $styleVal
|
||||
} else {
|
||||
Write-Warning "[specify] Warning: unknown commit_style '$styleVal' in git-config.yml (expected 'fixed' or 'conventional'); defaulting to 'fixed'"
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# Parse YAML to find auto_commit section
|
||||
$inAutoCommit = $false
|
||||
$inEvent = $false
|
||||
@@ -190,17 +140,6 @@ if ($d1 -eq 0 -and $d2 -eq 0 -and -not $untracked) {
|
||||
exit 0
|
||||
}
|
||||
|
||||
# In conventional mode, the commit message must be supplied by the agent
|
||||
# (via the GeneratedMessage argument); never fall back to the fixed message.
|
||||
if ($commitStyle -eq 'conventional') {
|
||||
if ($GeneratedMessage) {
|
||||
$commitMsg = $GeneratedMessage
|
||||
} else {
|
||||
Write-Warning "[specify] Error: commit_style is 'conventional' but no generated commit message was supplied; aborting auto-commit (pass -MessageFile <path>, or a raw message as arg 2, or set commit_style: fixed)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Derive a human-readable command name from the event
|
||||
$commandName = $EventName -replace '^after_', '' -replace '^before_', ''
|
||||
$phase = if ($EventName -match '^before_') { 'before' } else { 'after' }
|
||||
|
||||
@@ -565,12 +565,6 @@ if (-not $DryRun) {
|
||||
$env:SPECIFY_FEATURE = $branchName
|
||||
}
|
||||
|
||||
# Build the PowerShell-idiomatic persist hint, mirroring the core
|
||||
# create-new-feature.ps1 twin (and the bash/python twins of this script), which
|
||||
# all emit "# To persist in your shell: ...".
|
||||
$quotedBranchName = "'" + $branchName.Replace("'", "''") + "'"
|
||||
$featureAssignment = '$env:SPECIFY_FEATURE = ' + $quotedBranchName
|
||||
|
||||
if ($Json) {
|
||||
$obj = [PSCustomObject]@{
|
||||
BRANCH_NAME = $branchName
|
||||
@@ -587,6 +581,6 @@ if ($Json) {
|
||||
Write-Output "BRANCH_NAME: $branchName"
|
||||
Write-Output "FEATURE_NUM: $featureNum"
|
||||
if (-not $DryRun) {
|
||||
Write-Output "# To persist in your shell: $featureAssignment"
|
||||
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,15 +33,7 @@ def _value_after_colon(line: str) -> str:
|
||||
|
||||
|
||||
def _strip_quotes(value: str) -> str:
|
||||
"""Strip surrounding whitespace, then one leading quote and all trailing quotes.
|
||||
|
||||
Trimming first matters when the YAML value has trailing whitespace after a
|
||||
closing quote (``message: "Done" ``): stripping quotes anchored to the end
|
||||
of string would leave the closing quote dangling (``Done" ``) because the
|
||||
quote is no longer at the end. The PowerShell twin ``.Trim()``s before
|
||||
stripping, so trim here too to keep all three script variants in parity.
|
||||
"""
|
||||
value = value.strip()
|
||||
"""Strip one leading quote and all trailing quotes, mirroring the bash sed."""
|
||||
value = re.sub(r"^[\"']", "", value)
|
||||
return re.sub(r"[\"']*$", "", value)
|
||||
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-07-27T00:00:00Z",
|
||||
"updated_at": "2026-07-15T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/integrations/catalog.json",
|
||||
"integrations": {
|
||||
"alquimia": {
|
||||
"id": "alquimia",
|
||||
"name": "Alquimia AI",
|
||||
"version": "1.0.0",
|
||||
"description": "Alquimia AI CLI integration",
|
||||
"author": "spec-kit-core",
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"tags": ["alquimia"]
|
||||
},
|
||||
"claude": {
|
||||
"id": "claude",
|
||||
"name": "Claude Code",
|
||||
@@ -57,15 +48,6 @@
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"tags": ["ide"]
|
||||
},
|
||||
"droid": {
|
||||
"id": "droid",
|
||||
"name": "Factory Droid",
|
||||
"version": "1.0.0",
|
||||
"description": "Factory Droid CLI skills-based integration",
|
||||
"author": "spec-kit-core",
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"tags": ["cli", "skills", "factory"]
|
||||
},
|
||||
"amp": {
|
||||
"id": "amp",
|
||||
"name": "Amp",
|
||||
@@ -84,15 +66,6 @@
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"tags": ["cli", "skills"]
|
||||
},
|
||||
"command-code": {
|
||||
"id": "command-code",
|
||||
"name": "Command Code",
|
||||
"version": "1.0.0",
|
||||
"description": "Command Code CLI skills-based integration",
|
||||
"author": "spec-kit-core",
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"tags": ["cli", "skills"]
|
||||
},
|
||||
"devin": {
|
||||
"id": "devin",
|
||||
"name": "Devin for Terminal",
|
||||
|
||||
@@ -4,7 +4,7 @@ This edition covers Spec Kit activity in April 2026. Seventeen releases shipped
|
||||
|
||||
| **Spec Kit Core (Apr 2026)** | **Community & Content** | **SDD Ecosystem & Next** |
|
||||
| --- | --- | --- |
|
||||
| Seventeen releases shipped with major features: integration plugin architecture, workflow engine, preset composition, integration catalog, bundled lean preset, documentation site, and academic citation support. Three new agents added (Forgecode, Goose, Devin for Terminal). The repo grew from ~82k to **92,038 stars**. [\[github.com\]](https://github.com/github/spec-kit/releases) | Thoughtworks Technology Radar placed Spec Kit in the "Assess" ring. Community catalog grew from 26 to **83 extensions** and from 2 to **12 presets**. External coverage continued across developer blogs and industry press. XB Software documented a real legacy project. Fabián Silva shipped the Caramelo VS Code extension. | Matt Rickard argued for "smaller specs, harder checks." Will Torber's three-framework comparison recommended OpenSpec for most teams. The "Spec Layer" debate emerged: specs as constraint surfaces for AI agents. Spec Kit leads in breadth and portability; competitors differentiate on drift detection and orchestration depth. |
|
||||
| Seventeen releases shipped with major features: integration plugin architecture, workflow engine, preset composition, integration catalog, bundled lean preset, documentation site, and academic citation support. Three new agents added (Forgecode, Goose, Devin for Terminal). The repo grew from ~82k to **92,038 stars**. [\[github.com\]](https://github.com/github/spec-kit/releases) | Thoughtworks Technology Radar placed Spec Kit in the "Assess" ring. Community catalog grew from 26 to **83 extensions** and from 2 to **12 presets**. 12 substantive external articles published. XB Software documented a real legacy project. Fabián Silva shipped the Caramelo VS Code extension. | Matt Rickard argued for "smaller specs, harder checks." Will Torber's three-framework comparison recommended OpenSpec for most teams. The "Spec Layer" debate emerged: specs as constraint surfaces for AI agents. Spec Kit leads in breadth and portability; competitors differentiate on drift detection and orchestration depth. |
|
||||
|
||||
***
|
||||
|
||||
@@ -94,7 +94,7 @@ On **April 15**, the **Thoughtworks Technology Radar Volume 34** placed GitHub S
|
||||
|
||||
### Developer Articles and Blog Posts
|
||||
|
||||
April produced a steady stream of external articles.
|
||||
April produced 12 substantive external articles (plus one excluded as AI-generated SEO spam).
|
||||
|
||||
**Matt Rickard** published *"The Spec Layer: Why Spec-Driven Development (SDD) Works"* on April 1. His thesis: specs reduce execution freedom for AI agents, functioning as constraint surfaces. He compared Spec Kit, Kiro, OpenSpec, Tessl, Intent, and Symphony, and advocated for **"smaller specs, harder checks, less guessing."** [\[blog.matt-rickard.com\]](https://blog.matt-rickard.com/p/the-spec-layer)
|
||||
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
# Spec Kit - July 2026 Newsletter
|
||||
|
||||
This edition covers Spec Kit activity in July 2026 — a month of hardening and expanding the envelope. Twenty-eight releases shipped (v0.12.3 through v0.15.1), crossing three minor bumps and delivering three headline capabilities: the **`assess` "Idea Assessment Pipeline" extension**, which pushes spec-driven development *upstream* of the spec to answer "should we even build this?"; the new **`py` (Python) script type** and the broad shell→Python port that underpins it; and a **first-class agent-native runtime events layer** for integrations. Beneath the features, the month's dominant engineering theme was a sustained **security-hardening wave** — bounded HTTP reads, strict redirect validation, TOCTOU-race elimination, and defensive validation across the workflow engine. Externally, coverage broadened structurally: mainstream tech press (heise online) covered the v0.13 `assess` release in two languages, and a **companion-tooling ecosystem** bloomed around the project — spec↔code drift detectors, model-sizing advisors, and testing-gap tools all built *on top of* Spec Kit. A summary is in the table below, followed by details.
|
||||
|
||||
| **Spec Kit Core (Jul 2026)** | **Community & Content** | **SDD Ecosystem & Next** |
|
||||
| --- | --- | --- |
|
||||
| Twenty-eight releases shipped (v0.12.3–v0.15.1), crossing v0.13, v0.14, and v0.15. Headline features: the `assess` **Idea Assessment Pipeline** extension (capture→evidence→refine→design→go/clarify/kill), the new **`py` script type** plus a shell→Python port of the core scripts, git extension, and agent-context updater, and an **agent-native runtime events layer** for integrations. Three agents joined (Grok Build, Factory Droid CLI, Alquimia AI), the label-driven **bug-fix/bug-test** automation completed the triage pipeline, and a heavy **security-hardening** wave landed. The repo grew from ~117,400 to **124,655 stars**. [\[github.com\]](https://github.com/github/spec-kit/releases) | The community extension catalog grew from 125 to **144 entries**; presets reached **29**, community workflows **2**, bundles **1**. **heise online** covered the v0.13 `assess` release in English and German. Coverage shifted toward comparisons, companion tooling, and "who verifies the spec?" critiques. **~258 contributors** now listed. | A **companion-tooling ecosystem** emerged — artgraph (deterministic spec↔code drift), SpecJudge (model right-sizing), GAUNTLEX (security-testing gap), and custom skills like `speckit-next` and `prefill`. Comparisons increasingly pit Spec Kit against Kiro; balanced reviews keep flagging documentation proliferation and cognitive load, precisely the gaps the `assess` upstream step and the drift/companion ecosystem are built to close. |
|
||||
|
||||
***
|
||||
|
||||
> **Hardening the Foundation, Expanding the Envelope.** If June was defined by external validation, July was defined by internal consolidation and reach. No single release carried the weight of `converge` or `bundle`, but the month moved the project in two directions at once. It reached *upstream* — the new `assess` pipeline lets a team evaluate an idea (capture evidence, refine, design, then go/clarify/kill) *before* a spec exists, extending SDD past the spec into the decision to build. And it reached *down to the metal* — a new `py` script type and a systematic port of the core scripts, git extension, and agent-context updater from shell to Python, alongside a security-hardening wave that bounded every HTTP read, validated every redirect hop, eliminated file-race conditions, and taught the workflow engine to fail loudly instead of crashing on malformed input. Meanwhile the ecosystem answered the project's most-cited critique — "who verifies the spec, and who reads all this documentation?" — not with complaints but with *code*: a wave of companion tools built directly on Spec Kit artifacts. None of this happens without the community — the contributors, extension and preset authors, bundle builders, agent-integration maintainers, and practitioners writing in more than 20 languages. Thank you.
|
||||
|
||||
## Spec Kit Project Updates
|
||||
|
||||
### Releases Overview
|
||||
|
||||
**v0.12.3–v0.12.18** (July 1–17) was the month's longest patch run and carried two features amid heavy hardening. The **`py` script type** landed (#3285), adding Python interpreter resolution alongside the existing `sh`/`ps` options, and the **label-driven bug-fix (#3258) and bug-test (#3239) agentic workflows** completed the `bug-assess → bug-test → bug-fix` triage pipeline. The systematic shell→Python port began here: the **`update-agent-context` script** (#3387), the **git extension scripts** (#3400), and a **`check-prerequisites` proof-of-concept** (#3302) were all ported. **PyPI was documented as a first-class second install route** (#3516). New agents arrived — **Grok Build** (#3535) as a skills-based integration — while **Roo Code was retired** as a shut-down product (#3212). The rest was a broad defensive-validation sweep across the workflow engine (case-insensitive gate reject, quote-aware interpolation, host-less catalog-URL rejection, and dozens of "fail loudly on malformed input" guards). [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
**v0.13.0–v0.13.4** (July 17–22) delivered the month's headline feature: the **`assess` Idea Assessment Pipeline extension** (#3568), a pre-spec evaluation flow. The release also completed the Python port of the three core scripts — **`create-new-feature`, `setup-plan`, and `setup-tasks`** (#3386) — and added **Azure DevOps `az`-CLI token acquisition** hardening (#3527), **community bundle submission automation** (#3553), and the standalone **`WorkflowResolver`** refactor (#3557). **Factory Droid CLI** joined as an integration (#3587), **Bob was updated to a skills-based layout for Bob 2.0** (#3415), the **`pipeline` workflow** was added to the community catalog (#3338), and the **spec-of-specs feature-breakdown** approach was documented for handling complex features (#3648). [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
**v0.14.0–v0.14.4** (July 23–29) crossed a minor with a **security-hardening focus**: **bounded HTTP reads and strict redirect enforcement** (#3140, #3671), **secured extension/preset archive downloads** (#3141), and the **removal of the `shell` parameter from `run_command`** (#3716). The **git extension gained configurable Conventional Commit support** (#3413), the wheel now **bundles `scripts/python`** so `--script py` works from a clean install (#3665), and **Alquimia AI** joined as the month's third new agent (#2734). Documentation added a **Simplified Chinese README translation** (#3740), and the constitution stopped **propagating guidance into templates** (#3790). A long run of bundler, preset, and integration validation fixes rounded out the cluster. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
**v0.15.0–v0.15.1** (July 30–31) closed the month with a **first-class agent-native runtime events layer for integrations** (#3704) — the release's headline — plus a continued security pass: **TOCTOU-race elimination in file-unlink calls** (#3811, #3815, #3819), **UTF-8 encoding on registry file opens** (#3810, #3816), and **hardening of the extension URL-download cache against symlink/junction races** (#3869). Workflows gained the ability to **bind a gate verdict to a workflow input via `verdict_input`** (#3725), an opt-in **`constitution-sync` preset** shipped (#3873), the **`yolo` workflow** was added to the community catalog (#3864), and installs gained **tar-archive support** (#3874). [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### The Idea Assessment Pipeline: `assess`
|
||||
|
||||
July's headline feature was the **`assess` extension** (#3568), an *idea-assessment pipeline* that ships as an opt-in extension and extends spec-driven development one step further upstream than it has ever reached. Where the core lifecycle begins at `/speckit.specify` — assuming the decision to build has already been made — `assess` addresses the question that comes *before* the spec: **should this idea be built at all, and is it understood well enough to specify?**
|
||||
|
||||
The pipeline runs a staged flow — **capture → evidence → refine → design → decision** — that takes a raw idea, gathers supporting evidence, refines it into something concrete, sketches a design, and terminates in an explicit **go / clarify / kill** verdict. A `go` feeds a well-formed problem into the existing `/speckit.specify` step; a `clarify` routes back for more information; a `kill` stops work before a line of spec is written. Its input is just an idea — pasted text, a URL, a ticket, or a codebase pointer — so the pipeline works **equally well on an empty, freshly-initialized project or on an existing codebase** (#3732); a team can evaluate a green idea before any scaffolding exists, or assess a change against a repo that already has one.
|
||||
|
||||
The feature drew the month's most prominent mainstream-press coverage: **heise online** ran *"From Idea to Spec: The New Feature in Spec Kit 0.13"* in both English and German, framing `assess` as the notable addition of the 0.13 line alongside the Azure DevOps CLI support and the bundler/preset validation fixes. Coming from a major European technology outlet rather than a developer blog, it was a signal that Spec Kit's release cadence is now tracked as mainstream tooling news. [\[heise.de\]](https://www.heise.de/en/news/From-Idea-to-Spec-The-New-Feature-in-Spec-Kit-0-13-11371866.html)
|
||||
|
||||
### The Python Migration: the `py` Script Type
|
||||
|
||||
Spec Kit's second July theme was quieter but structurally important: the project began migrating its shell scripts to **Python**. The new **`py` script type** (#3285) joins `sh` (bash) and `ps` (PowerShell) as a third option at `specify init`, backed by Python-interpreter resolution that skips broken stubs (including the Windows Store `python3` alias, #3385). The `py` type is the project's answer to the perennial bash/PowerShell parity tax — every script fix previously had to be written twice and kept in sync, a recurring source of the Windows-parity bugs that filled prior months' changelogs.
|
||||
|
||||
Behind the new type, a systematic port landed piece by piece across the month: the **`update-agent-context`** updater (#3387), the **git extension scripts** (#3400), a **`check-prerequisites`** proof-of-concept (#3302), and finally the three core scripts — **`create-new-feature`, `setup-plan`, and `setup-tasks`** (#3386). The wheel was updated to bundle `scripts/python` so `--script py` works from a clean PyPI install (#3665), and the installation docs and init option table were updated to document the new type and the sh/ps migration plan (#3284, #3640). The end state is a single, cross-platform script implementation that removes an entire class of parity bugs. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### Agent-Native Runtime Events
|
||||
|
||||
The v0.15.0 headline was a **first-class agent-native runtime events layer for integrations** (#3704). It bridges Spec Kit to the host agent's own lifecycle via a set of canonical, snake_case event names — `session_start`, `pre_tool_use`, `post_tool_use`, `user_prompt_submit`, `stop`, and `session_end`. A lightweight, zero-dependency **Event Dispatcher** (`.specify/events.py`) is scaffolded during `specify init`, and per-integration **Event Adapters** translate each canonical event into the agent's *native* hook configuration — `.github/hooks/speckit.json` (bash/PowerShell variants) for **Copilot CLI**, `.claude/settings.json` for Claude Code, `.cursor/hooks.json` for Cursor, `.codex/config.toml` for Codex, a TypeScript plugin for opencode, and native settings merges for Gemini, Qwen, Devin, and Tabnine — so extension authors declare `events:` in `extension.yml` once and never learn agent-specific names. Resolution is a four-tier stack (CLI `--events false` → user `.specify/integration-events.yml` override → extension-declared events → built-in defaults), and multiple extensions declaring the same event all run. The change accompanied a broader integration-refinement run: agents that use an always-slash invocation (Droid, Forge, Cline) now render hyphenated `/speckit-<name>` commands correctly (#3688, #3642, #3622), native skill-invocation prefixes are preserved (#3663), and several agents (kiro-cli, Lingma, Pi, omp) were declared multi-install-safe. The through-line is that integrations are increasingly *native* to each agent rather than a lowest-common-denominator overlay. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### The Security-Hardening Wave
|
||||
|
||||
The dominant engineering theme across all twenty-eight releases was **security and robustness**. The work fell into three bands. **Bounded I/O:** every catalog, download, and bundle HTTP response is now read under a byte cap with strict redirect validation on every hop (#3140, #3671, #3763, #3141), closing a class of unbounded-read / DoS exposure. **Race elimination:** TOCTOU races in file-unlink and state-file handling were removed (#3811, #3815, #3819), the extension URL-download cache was hardened against symlink and junction races (#3869), and registry file opens were pinned to UTF-8 (#3810, #3816). **Injection and input hardening:** the `shell` parameter was removed from `run_command` (#3716), user-supplied catalog metadata is escaped in every discovery/list/`init` output path (#3772, #3773, #3774, #3806, #3826, #3863), and catalog URLs are re-validated *after* redirects to preserve HTTPS/host guarantees (#3523, #3524).
|
||||
|
||||
Running alongside this was a systematic **"fail loudly, don't crash"** campaign across the workflow engine and catalog loaders: dozens of PRs replaced raw `ValueError`/`OverflowError`/crash paths with clean validation errors on malformed input — non-string commands, prompts, integrations, and models; non-list branches and `wait_for` entries; `priority: .inf` and boolean priorities; non-mapping manifest blocks; and superscript-digit gate prompts. The entire month's hardening arrived as prevention rather than response. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### The Workflow Engine & Bundles Mature
|
||||
|
||||
Beyond hardening, the **workflow engine** kept gaining capability. Steps can now read their **workflow source directory** (#3469), the shell and prompt steps got **configurable, validated timeouts** (#3404, #3847, #3768), a **gate verdict can bind to a workflow input** via `verdict_input` (#3725), and the **`WorkflowResolver`** was extracted as a standalone component (#3557). Two community workflows reached the catalog — the guided **`pipeline`** (#3338, which chains into the core `/speckit.converge`) and **`yolo`** (#3864) — bringing the standalone-workflow count to two.
|
||||
|
||||
The **bundle subsystem** introduced in June matured through a long tail of correctness work — reproducible builds via canonical POSIX arcnames (#3658), literal UTF-8 manifest dumps (#3660), strict rejection of malformed `requires`/`provides`/`integration`/`catalogs` blocks, and a clean `BundlerError` on malformed download URLs (#3586). **Community bundle submission automation** landed (#3553) and the **SicarioSpec Security & Governance Bundle** became a cataloged community bundle (#3636), making bundles a live community-submittable artifact type in practice. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### Agent Integrations
|
||||
|
||||
The agent portfolio grew net **+3 to 37 integrations**. Three joined — **Grok Build** (#3535), **Factory Droid CLI** (#3587, closing the 300+-day #822), and **Alquimia AI** (#2734) — while **Roo Code** was retired as a shut-down product (#3212). **Bob** was migrated to a skills-based layout for Bob 2.0 (#3415), **Kilocode** now installs commands under `.kilo/commands` (#3672), and a broad correctness pass fixed hyphenated-command rendering and dispatch for the always-slash agents (Droid, Forge, Cline) and preserved native skill-invocation prefixes (#3663). The pattern continues from June — pruning dead products while making the surviving integrations more native to each agent. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
### The Extension & Preset Ecosystem
|
||||
|
||||
The community extension catalog grew from 125 to **144 entries** during July — nineteen net additions. Community presets grew from 23 to **29**, community workflows reached **2**, and the first community **bundle** (SicarioSpec) was cataloged.
|
||||
|
||||
Notable new extensions by category:
|
||||
|
||||
- **Verification, drift & evidence**: Test Coverage Drift Control, PatchWarden Evidence Pack, Quality Gates (Enforcement Layer), Verify Review Ship, Intent Reconciliation
|
||||
- **Requirements & intake**: EARS Requirements Syntax, the `assess` Idea Assessment Pipeline, Spec-Kit BDD, Charter
|
||||
- **External trackers & round-trip**: Linear Weave, Multi-Repo Branch Sync, ContextForge MCP
|
||||
- **Design & docs**: Spec Kit Figma, Figma Starter, Blueprint Index — Living Architecture Map, LLM Wiki, Dotdog
|
||||
- **Knowledge & orchestration**: OKF Knowledge Bundle Generator, Orchestration Task Context Management, Spec Kit Memory
|
||||
|
||||
The catalog also showed strong maintenance activity: **DocGuard — CDD Enforcement** advanced through several releases (to v0.33.0), **Verify Review Ship** and **Quality Gates (Enforcement Layer)** iterated rapidly, and **Architecture Guard**, **Golden Demo**, **Coding Standards Drift Control**, **Ripple**, and the **Ralph Loop** all shipped updates. The preset side was the month's busiest: a large **governance-preset** family expanded and iterated — the **Autonomous Run Governance** and **Parallel Autonomous Run Governance** presets, a full **Intake** governance suite (Authoring, Review, Sequencing), **Test-First Governance**, and coordinated version bumps across the A11Y, Agent-Parity, Cross-Platform, iSAQB-Architecture, Architecture, and Security governance presets. [\[github.com\]](https://github.github.io/spec-kit/community/extensions.html)
|
||||
|
||||
### Documentation & Docs Site
|
||||
|
||||
July's documentation work paired the new features with a landing-page refresh. The **spec-of-specs feature-breakdown** approach was documented for handling complex features (#3648), the **`py` (Python) script type** was documented in the installation guide and init option table (#3284, #3625, #3640), and the **`__SPECKIT_COMMAND`** token for portable cross-command references was documented (#3503). The landing page was reframed to weave the **harness/SDLC framing** and modernize the install and positioning story (#3565, #3567), ecosystem stats were refreshed (#3561), and **`extensions.yml` hook configuration** was documented (#3563). Upgrade guidance clarified that project-file upgrades flow through `integration upgrade` / `extension update` (#3326) and that Claude Code files live in `.claude/skills` (#3708). [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
|
||||
## Community & Content
|
||||
|
||||
### Press and Industry Coverage
|
||||
|
||||
July's coverage shifted from "what is SDD" explainers toward tool comparisons, companion tooling, and pointed "who verifies the spec?" critiques. No first-party Microsoft or GitHub post appeared in July; the nearest remained June's Microsoft Developer Blog piece.
|
||||
|
||||
**heise online** (Wolf Hosbach, July 21) was the month's most prominent mainstream-press coverage, publishing *"From Idea to Spec: The New Feature in Spec Kit 0.13"* in both English and German — news coverage of the `assess` Idea Assessment Pipeline, the Azure DevOps CLI support, and the 0.13 validation fixes. Mainstream European tech press now tracks Spec Kit's minor releases as tooling news. [\[heise.de\]](https://www.heise.de/en/news/From-Idea-to-Spec-The-New-Feature-in-Spec-Kit-0-13-11371866.html)
|
||||
|
||||
**Towards AI** (Rost Glukhov, July 12) compared **GitHub Spec Kit vs Kiro vs Claude Code** on SDD workflow rather than model capability, part of a July-long current of "which SDD tool?" comparisons that increasingly pit Spec Kit specifically against Kiro. [\[pub.towardsai.net\]](https://pub.towardsai.net/github-spec-kit-vs-kiro-vs-claude-code-sdd-workflows-a9e7fab3e545)
|
||||
|
||||
**ranjankumar.in** (Ranjan Kumar, July 13) argued that four SDD frameworks — BMAD, Spec Kit, Kiro, and Superpowers — converge on the same structural "invariants," engaging Spec Kit's actual internals (`workflows.md`, run-state `state.json`) rather than treating it as a black box. [\[ranjankumar.in\]](https://ranjankumar.in/spec-driven-development-invariants-not-frameworks)
|
||||
|
||||
Release-trackers continued their factual coverage of the 0.13–0.15 run, and **Level Up Coding** (JingJing "Chris" Bao) published a three-part practitioner series on Spec Kit's presets, extensions, and pipeline/workflow features as the path beyond linear slash-commands. [\[levelup.gitconnected.com\]](https://levelup.gitconnected.com/from-linear-commands-to-automated-pipelines-how-spec-kit-orchestrates-nonlinear-ai-development-55ca03c5617b)
|
||||
|
||||
### The Companion-Tooling Ecosystem
|
||||
|
||||
July's most telling signal was not an article but a pattern: independent developers responded to Spec Kit's most-cited critiques by **building tools on top of it**. The recurring complaint — documentation proliferation and "who verifies the generated spec?" — turned into code.
|
||||
|
||||
- **artgraph** (mori-shin, July 20) — a deterministic, hash-based spec↔code drift-detection CLI with an `artgraph integrate speckit` hook, built specifically to give Spec Kit's LLM-prompt-based verification a deterministic backstop. [\[zenn.dev\]](https://zenn.dev/mrmtsntr/articles/artgraph-spec-code-drift)
|
||||
- **SpecJudge** (Joaquín Ruiz, July 21) — a companion CLI that reads Spec Kit's constitution/spec/tasks artifacts to recommend a *right-sized* model for the project. [\[dev.to\]](https://dev.to/jokiruiz/specjudge-which-ai-model-is-right-sized-for-your-project-ask-your-specs-2edp)
|
||||
- **GAUNTLEX** (Sanjoy Ghosh, July 16) — named Spec Kit a leading SDD tool while arguing SDD leaves a security-testing gap, and shipped a tool to fill it. [\[hashnode.dev\]](https://sanjoy1234.hashnode.dev/the-testing-gap-nobody-s-talking-about-in-spec-driven-development)
|
||||
- **Custom skills** — [`speckit-next`](https://qiita.com/htcd/items/ec76f2b7194be3297b93) (htcd, July 31), a skill that recommends the next command because the names and order are hard to remember, and [`prefill`](https://velog.io/@k3nta/ai-adoption-journey-1-tools) (k3nta, July 1), a skill that patches `clarify`'s blind spots.
|
||||
|
||||
Together these are the clearest evidence yet that Spec Kit has become a *platform* — its artifacts are stable enough, and its gaps well-enough understood, that a third-party tooling layer is forming around it. [\[zenn.dev\]](https://zenn.dev/mrmtsntr/articles/artgraph-spec-code-drift)
|
||||
|
||||
### Developer Articles and Blog Posts
|
||||
|
||||
July's articles skewed heavily multilingual — strong hands-on series in Japanese, Chinese, and Korean — with a clear thread of honest, use-it-in-anger critique.
|
||||
|
||||
Notable articles:
|
||||
|
||||
- **ta_kawano** (note.com, July 28–31) published a consolidated four-part **Kiro vs Spec Kit** head-to-head, completing a 108-task / 301-test build with Spec Kit where Kiro ran out of credit, praising measurable Success Criteria and auto-listed edge cases while flagging ~15,000 lines of generated documentation — and reframing Spec Kit as a requirements-elicitation tool. [\[note.com\]](https://note.com/takawano/n/ncb552ee37331)
|
||||
- **magebyte / 码哥字节** (SegmentFault, July 26) built a Go REST API through the five-step workflow, covered the three spec-persistence models and the extension/preset system, and claimed ~80% less AI "hallucination" rework. [\[segmentfault.com\]](https://segmentfault.com/a/1190000048082146)
|
||||
- **Nil Seri** (Medium, July 16) published a brownfield guide adding Spec Kit to an existing Spring Boot / Maven project with Jira and Confluence integration — "from Jira ticket to verified code." [\[medium.com\]](https://medium.com/@senoritadeveloper/using-spec-kit-in-an-existing-spring-boot-maven-project-from-jira-ticket-to-verified-code-4e6da99b5d19)
|
||||
- **kitroc7134** (Qiita, July 4) tested `/speckit.converge` with deliberate fault-injection on a FastAPI Todo API — detect drift → append convergence tasks → re-implement — validating June's convergence loop in the field. [\[qiita.com\]](https://qiita.com/kitroc7134/items/117d4839f259bc403626)
|
||||
- **yutakaosada** (Zenn, July 25) — a Microsoft-MVP .NETラボ talk that uses Spec Kit but candidly flags AI-credit consumption, over-production of docs, and single-source-of-truth collapse, comparing it with Copilot Plan mode. [\[zenn.dev\]](https://zenn.dev/yutakaosada/articles/70e01981647159)
|
||||
|
||||
Additional coverage appeared on TechWealthBuzz, Hashnode, TabNews-adjacent outlets, CSDN and 腾讯云 (Chinese), Naver/velog/Tistory (Korean), and Qiita/note (Japanese) — including several "is it too heavy?" and documentation-proliferation critiques, and a [Korean instructor's piece](https://blog.naver.com/gaussian88/224363268926) citing Spec Kit's star growth from ~90k in May to ~120k in July. [\[note.com\]](https://note.com/takawano/n/ncb552ee37331)
|
||||
|
||||
### Community Growth by the Numbers
|
||||
|
||||
| Metric | Start of July | End of July | Change |
|
||||
| --- | --- | --- | --- |
|
||||
| GitHub stars | 117,423 | 124,655 | +7,232 (+6%) |
|
||||
| Forks | 10,382 | 11,125 | +743 |
|
||||
| Contributors | 245 | ~258 | +~13 |
|
||||
| Releases (total) | 177 | 205 | +28 (v0.12.3–v0.15.1) |
|
||||
| Community extensions | 125 | 144 | +19 |
|
||||
| Community presets | 23 | 29 | +6 |
|
||||
| Community workflows | 1 | 2 | +1 |
|
||||
| Community bundles | 1 | 1 | steady |
|
||||
| Agent integrations | 34 | 37 | +3 (net) |
|
||||
| Discussions (open) | 457 | ~467 | +~10 |
|
||||
|
||||
## SDD Ecosystem & Industry Trends
|
||||
|
||||
### From Tool to Platform
|
||||
|
||||
July's clearest ecosystem signal was structural: the conversation moved from "how do I use Spec Kit?" to "what do I build *around* it?" The companion tools — artgraph for deterministic drift, SpecJudge for model sizing, GAUNTLEX for the testing gap, and a growing set of custom agent skills — treat Spec Kit's artifacts (constitution, spec, tasks, run-state) as a stable substrate to build against. The public community catalog reinforces the point: the loudest theme across the 144 cataloged extensions is verification and quality (review, validate, drift, sync, verify, audit), and core SDD verbs are increasingly *re-expressed* by extensions rather than merely overridden — evidence of demand for composable, overridable core commands. [\[github.com\]](https://github.github.io/spec-kit/community/extensions.html)
|
||||
|
||||
### Competitive Landscape
|
||||
|
||||
The "which SDD tool?" comparison remained the dominant content genre, but July's framing narrowed: where June's surveys ran a seven-tool field, July's most substantive pieces increasingly went head-to-head **Spec Kit vs Kiro** (ta_kawano, [faruryo](https://qiita.com/faruryo/items/87a14728299e89f80ff4), Towards AI). The recurring verdict held — Spec Kit is the heaviest and most flexible option, strong on measurable Success Criteria, requirements elicitation, and greenfield decomposition, while its documentation proliferation and cognitive load are the consistent trade-off. The convergence-invariants analyses (ranjankumar.in) went further, arguing the frameworks are converging on the same structural primitives, which shifts the competitive question from "which tool" to "which ecosystem and governance model." On that axis, Spec Kit's widening catalog, agent-neutrality, and now a forming companion-tooling layer are its differentiators. [\[note.com\]](https://note.com/takawano/n/ncb552ee37331)
|
||||
|
||||
## Roadmap
|
||||
|
||||
Areas under discussion or in progress for future development:
|
||||
|
||||
- **Upstream of the spec** — the `assess` Idea Assessment Pipeline extends SDD before the spec exists. Expect the capture→evidence→refine→design→decision flow to deepen, and the boundary between idea assessment and `/speckit.specify` to be a key area to refine as the pipeline sees real use. [\[heise.de\]](https://www.heise.de/en/news/From-Idea-to-Spec-The-New-Feature-in-Spec-Kit-0-13-11371866.html)
|
||||
- **The Python migration** — the `py` script type and the port of the core scripts, git extension, and agent-context updater establish Python as the path out of the bash/PowerShell parity tax. Completing the port and making `py` a well-trodden default (rather than sh/ps) is the payoff: an entire class of Windows-parity bugs disappears. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
- **Agent-native runtime events** — the first-class events layer lets integrations wire Spec Kit into each agent's own runtime through canonical event names and per-agent adapters. The layer is **actively evolving** — early signals point to opencode context injection and JSON-envelope agent hooks. Expect more agents to gain event adapters and extension authors to lean on the declarative `events:` surface as the integration layer shifts from lowest-common-denominator overlay to genuinely native behavior. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
- **Copilot skills as the default** — July shipped a warning ahead of the skills-default rollout, and the signals now point to the **default flip being in progress** — moving `specify init --integration copilot` to the skills-based layout and making the default init integration overridable via an environment variable, with the markdown-command layout becoming the legacy path. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
- **A Copilot-native surface** — the first-party [`github/spec-kit-copilot`](https://github.com/github/spec-kit-copilot) repo (a listed community friend) wraps the `specify` CLI as a **Copilot skills plugin** (nine skills across setup, init, extensions, presets, bundles, workflows, and self-upgrade) for the Copilot CLI and App, aligned to CLI v0.15.0. The emerging direction is a **visual, Copilot-driven surface** — early work explores canvas dashboards for the Spec-Driven Development flow, a Bug Fix Pipeline, and `assess` — turning the CLI's flows into an interactive layer. [\[github.com\]](https://github.com/github/spec-kit-copilot)
|
||||
- **The companion-tooling layer** — artgraph, SpecJudge, GAUNTLEX, and custom skills signal a third-party ecosystem forming on Spec Kit artifacts. The open question is whether the project absorbs these patterns (as it did drift → `converge`) or leaves them to the ecosystem; the verification/drift demand in the extension catalog suggests continued upstream pull. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
- **Security and robustness as a standing discipline** — July's hardening wave (bounded reads, strict redirects, TOCTOU elimination, fail-loudly validation) shifted from feature to routine, and the signals point to it **continuing as an ongoing campaign** — a stdin read cap to close a DoS path and a broader atomicity push (atomic temp-file writes, subprocess timeouts, structured logging, and narrowed exception handlers). Sustaining the no-unbounded-read invariant as the surface (bundles, workflows, catalogs, events) grows is the ongoing work. [\[github.com\]](https://github.com/github/spec-kit/releases)
|
||||
- **Experience simplification** — documentation proliferation and cognitive load remain the single most-cited concern across July's balanced reviews (ta_kawano, yutakaosada, and multiple Japanese/Korean pieces). The `assess` upstream gate, the lean/TinySpec presets, `/speckit.converge`, and the forming companion-tooling layer all provide answers; surfacing them to new users is the persistent opportunity. [\[note.com\]](https://note.com/takawano/n/ncb552ee37331)
|
||||
@@ -1,14 +1,14 @@
|
||||
# Spec Kit - June 2026 Newsletter
|
||||
|
||||
This edition covers Spec Kit activity in June 2026 — a month of maturation and mainstream validation. Twenty-five releases shipped (v0.9.0 through v0.12.2), spanning four minor bumps and delivering two headline capabilities: the **`/speckit.converge` command**, which closes the loop between a spec and the code that implements it, and the new **`specify bundle` subsystem**, a role-based distribution layer that composes extensions, presets, workflows, and steps into a single installable unit. The workflow engine became programmable, the git extension went opt-in as the first real breaking change, and the ecosystem crossed **120+ community extensions**. Externally, June brought broad validation — Microsoft's own Developer Blog published a first-party spec-driven development post, an enterprise reported 2–4× velocity gains, and coverage spanned dozens of languages. A summary is in the table below, followed by details.
|
||||
This edition covers Spec Kit activity in June 2026 — a month of maturation and mainstream validation. Twenty-five releases shipped (v0.9.0 through v0.12.2), spanning four minor bumps and delivering two headline capabilities: the **`/speckit.converge` command**, which closes the loop between a spec and the code that implements it, and the new **`specify bundle` subsystem**, a role-based distribution layer that composes extensions, presets, workflows, and steps into a single installable unit. The workflow engine became programmable, the git extension went opt-in as the first real breaking change, and the ecosystem crossed **120+ community extensions**. Externally, June was the highest-volume press month on record — Microsoft's own Developer Blog published a first-party spec-driven development post, an enterprise reported 2–4× velocity gains, and 75 substantive articles appeared across 25+ languages. A summary is in the table below, followed by details.
|
||||
|
||||
| **Spec Kit Core (Jun 2026)** | **Community & Content** | **SDD Ecosystem & Next** |
|
||||
| --- | --- | --- |
|
||||
| Twenty-five releases shipped (v0.9.0–v0.12.2) with key features: the `/speckit.converge` convergence loop, the `specify bundle` role-based packaging subsystem, a programmable workflow engine (step catalog, JSON output, `from_json`), the git extension becoming opt-in (`--no-git` removed), and six new agents (Cline, rovodev, Zed, Firebender, ZCode, omp). The repo grew from ~107k to **~116,500 stars**. [\[github.com\]](https://github.com/github/spec-kit/releases) | The community extension catalog grew from 105 to **124 entries**; presets reached **23**. Microsoft's Developer Blog published a first-party SDD post naming Spec Kit as the operationalizing toolkit. Press coverage spanned dozens of languages. **245 contributors** now listed. | An enterprise (SNCF Connect & Tech) reported **2–4× velocity** from SDD. Analysts and comparisons increasingly name Spec Kit "the category anchor" and agent-neutral default. Competitors differentiate on brownfield and drift; balanced reviews continue to flag review-overload and ceremony for small tasks. |
|
||||
| Twenty-five releases shipped (v0.9.0–v0.12.2) with key features: the `/speckit.converge` convergence loop, the `specify bundle` role-based packaging subsystem, a programmable workflow engine (step catalog, JSON output, `from_json`), the git extension becoming opt-in (`--no-git` removed), and six new agents (Cline, rovodev, Zed, Firebender, ZCode, omp). The repo grew from ~107k to **~116,500 stars**. [\[github.com\]](https://github.com/github/spec-kit/releases) | The community extension catalog grew from 105 to **124 entries**; presets reached **23**. Microsoft's Developer Blog published a first-party SDD post naming Spec Kit as the operationalizing toolkit. June was the highest-volume press month yet — **75 substantive articles** across 25+ languages. **245 contributors** now listed. | An enterprise (SNCF Connect & Tech) reported **2–4× velocity** from SDD. Analysts and comparisons increasingly name Spec Kit "the category anchor" and agent-neutral default. Competitors differentiate on brownfield and drift; balanced reviews continue to flag review-overload and ceremony for small tasks. |
|
||||
|
||||
***
|
||||
|
||||
> **Spec-Driven Development, Institutionalized.** If May was defined by milestone 100s, June was defined by validation from outside the project. Microsoft's own Developer Blog published a first-party post presenting spec-driven development and positioning Spec Kit as the toolkit that operationalizes it. An enterprise — SNCF Connect & Tech — went on the record with **2–4× velocity gains** from adopting SDD. Coverage appeared in dozens of languages, and the recurring verdict across independent comparisons was that Spec Kit is "the category anchor" and the agent-neutral default. Meanwhile the core matured from v0.9 to v0.12: the workflow engine became genuinely programmable, the first real breaking change shipped, and the new convergence loop and bundle subsystem gave the project answers to its two most-cited gaps — drift and distribution. None of this happens without the community — the contributors, extension and preset authors, bundle builders, and practitioners writing in a dozen languages. Thank you.
|
||||
> **Spec-Driven Development, Institutionalized.** If May was defined by milestone 100s, June was defined by validation from outside the project. Microsoft's own Developer Blog published a first-party post presenting spec-driven development and positioning Spec Kit as the toolkit that operationalizes it. An enterprise — SNCF Connect & Tech — went on the record with **2–4× velocity gains** from adopting SDD. A record **75 substantive articles** appeared in more than 25 languages, and the recurring verdict across independent comparisons was that Spec Kit is "the category anchor" and the agent-neutral default. Meanwhile the core matured from v0.9 to v0.12: the workflow engine became genuinely programmable, the first real breaking change shipped, and the new convergence loop and bundle subsystem gave the project answers to its two most-cited gaps — drift and distribution. None of this happens without the community — the contributors, extension and preset authors, bundle builders, and practitioners writing in a dozen languages. Thank you.
|
||||
|
||||
## Spec Kit Project Updates
|
||||
|
||||
@@ -84,7 +84,7 @@ On **June 10**, the **Microsoft Developer Blog** published *"Spec-Driven Develop
|
||||
|
||||
### Press and Industry Coverage
|
||||
|
||||
June's press coverage spanned dozens of languages and platforms.
|
||||
June was the **highest-volume coverage month on record — 75 substantive articles** across more than 25 languages.
|
||||
|
||||
**Xebia / XPRT Magazine #21** (Hidde de Smet & Emanuele Bartolesi, June 17) published a 32-minute full six-command walkthrough covering both greenfield and brownfield, honest about markdown-review overhead and where spec quality becomes the bottleneck. [\[xebia.com\]](https://xebia.com/blog/building-software-with-spec-kit/)
|
||||
|
||||
@@ -102,7 +102,7 @@ June's press coverage spanned dozens of languages and platforms.
|
||||
|
||||
### Developer Articles and Blog Posts
|
||||
|
||||
June's coverage skewed heavily multilingual, with deep hands-on series in Chinese, Japanese, and Korean, and a strong current of "which tool should I choose?" comparisons.
|
||||
June's 75 articles skewed heavily multilingual, with deep hands-on series in Chinese, Japanese, and Korean, and a strong current of "which tool should I choose?" comparisons.
|
||||
|
||||
Notable English-language articles:
|
||||
|
||||
@@ -137,7 +137,7 @@ Coverage also appeared on TabNews (Portuguese), Habr and CSDN, note.com, Substac
|
||||
|
||||
### The Category Consolidates
|
||||
|
||||
Across June's broad article coverage, a consistent framing emerged: spec-driven development is now an established category, and Spec Kit is its reference implementation. SSOJet called it "the category anchor," Design News and multiple comparison pieces called it the agent-neutral default, and ToolTwist's CxO guide named it the "safe default for scaling teams." The Microsoft Developer Blog post and the SNCF enterprise interview extended that framing beyond the developer press into institutional and enterprise contexts. [\[ssojet.com\]](https://ssojet.com/blog/best-spec-driven-development-tools)
|
||||
Across June's record article volume, a consistent framing emerged: spec-driven development is now an established category, and Spec Kit is its reference implementation. SSOJet called it "the category anchor," Design News and multiple comparison pieces called it the agent-neutral default, and ToolTwist's CxO guide named it the "safe default for scaling teams." The Microsoft Developer Blog post and the SNCF enterprise interview extended that framing beyond the developer press into institutional and enterprise contexts. [\[ssojet.com\]](https://ssojet.com/blog/best-spec-driven-development-tools)
|
||||
|
||||
### Competitive Landscape
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ This edition covers Spec Kit activity in May 2026 — a month defined by three m
|
||||
|
||||
| **Spec Kit Core (May 2026)** | **Community & Content** | **SDD Ecosystem & Next** |
|
||||
| --- | --- | --- |
|
||||
| Fourteen releases shipped with key features: multi-install for concurrent agent integrations, constitution governance in implement, authentication provider registry, Hermes and Lingma agents, and a `__init__.py` decomposition series. The repo grew from ~92k to **106,951 stars**, crossing **100K** on May 21. [\[github.com\]](https://github.com/github/spec-kit/releases) | The community extension catalog crossed **100 entries** (now 105). Open Source Friday livestream drove a press wave: Visual Studio Magazine, DevOps.com, MarkTechPost, HackerNoon, and many more across multiple languages. **217 contributors** now listed. | MarkTechPost called Spec Kit "the most community-adopted open-source option" for SDD. The Futurum Group's Mitch Ashley framed specs as "the unit of governance across agents and contributors." Truong Phung published a 61-min production playbook referencing Spec Kit. Competitors grew but differentiate on orchestration; Spec Kit leads in portability and community. |
|
||||
| Fourteen releases shipped with key features: multi-install for concurrent agent integrations, constitution governance in implement, authentication provider registry, Hermes and Lingma agents, and a `__init__.py` decomposition series. The repo grew from ~92k to **106,951 stars**, crossing **100K** on May 21. [\[github.com\]](https://github.com/github/spec-kit/releases) | The community extension catalog crossed **100 entries** (now 105). Open Source Friday livestream drove a press wave: Visual Studio Magazine, DevOps.com, MarkTechPost, HackerNoon, and 25+ more articles — now tracked across multiple languages following an expanded discovery methodology. **217 contributors** now listed. | MarkTechPost called Spec Kit "the most community-adopted open-source option" for SDD. The Futurum Group's Mitch Ashley framed specs as "the unit of governance across agents and contributors." Truong Phung published a 61-min production playbook referencing Spec Kit. Competitors grew but differentiate on orchestration; Spec Kit leads in portability and community. |
|
||||
|
||||
***
|
||||
|
||||
@@ -76,7 +76,7 @@ May produced the broadest press coverage to date, with publications from the mai
|
||||
|
||||
### Developer Articles and Blog Posts
|
||||
|
||||
May produced a wave of independent coverage across many languages.
|
||||
May produced a wave of independent coverage — well beyond any previous month. Starting this month, article discovery was expanded beyond English-centric search engines to include language-appropriate engines for 25+ languages, so the broader coverage partly reflects wider discovery rather than a sudden spike.
|
||||
|
||||
Notable non-English coverage:
|
||||
|
||||
|
||||
@@ -59,19 +59,6 @@ Content resolution functions for composition:
|
||||
- **Bash**: `resolve_template_content()` in `scripts/bash/common.sh` (templates only; command/script composition is handled by the Python resolver)
|
||||
- **PowerShell**: `Resolve-TemplateContent` in `scripts/powershell/common.ps1` (templates only; command/script composition is handled by the Python resolver)
|
||||
|
||||
### Constitution lifecycle
|
||||
|
||||
Initialization resolves `constitution-template` through the full stack and seeds
|
||||
`.specify/memory/constitution.md` once. Existing files are preserved byte-for-byte. On subsequent
|
||||
`/constitution` runs, the command resolves the current composed template at runtime and uses the live
|
||||
constitution as the source of project-specific values and amendments.
|
||||
|
||||
Preset installation, removal, enablement, disablement, and priority changes do not materialize
|
||||
`constitution-template` by default. When the enabled preset registry contains `constitution-sync`,
|
||||
those operations may reconcile the live file, but only if its provenance hash proves it is still
|
||||
generated content. Missing files may be seeded when the preset is installed; authored or edited
|
||||
constitutions are never overwritten.
|
||||
|
||||
## 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`.
|
||||
|
||||
@@ -300,12 +300,6 @@ git push origin add-your-preset
|
||||
|
||||
## Verification Process
|
||||
|
||||
> **How submissions get picked up:** the automated catalog-validation workflow only runs
|
||||
> once the `preset-submission` label is on the issue. On this public repository, contributors
|
||||
> cannot apply that label themselves — a maintainer applies it during issue triage. Until then
|
||||
> the issue simply waits in triage; there is no action required from you, and there is no need to
|
||||
> re-request the label in a comment.
|
||||
|
||||
After submission, maintainers will review:
|
||||
|
||||
1. **Manifest validation** — valid `preset.yml`, all files exist
|
||||
|
||||
@@ -15,16 +15,6 @@ If no preset is installed, core templates are used — exactly the same behavior
|
||||
|
||||
Template resolution happens **at runtime** — although preset files are copied into `.specify/presets/<id>/` during installation, Spec Kit walks the resolution stack on every template lookup rather than merging templates into a single location.
|
||||
|
||||
`constitution-template` follows the same runtime model. Project initialization seeds
|
||||
`.specify/memory/constitution.md` once so downstream commands always have a constitution to read.
|
||||
After that, installing, removing, enabling, disabling, or reprioritizing presets does not rewrite the
|
||||
live constitution. Each `/constitution` run resolves the current composed `constitution-template`,
|
||||
then applies existing project values and amendments to that scaffold.
|
||||
|
||||
Teams that intentionally want preset stack changes to refresh an unchanged generated constitution can
|
||||
install the bundled `constitution-sync` preset. It restores guarded install-time materialization in
|
||||
addition to its command-time propagation behavior; authored constitutions remain protected.
|
||||
|
||||
For detailed resolution and command registration flows, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
## Command Overrides
|
||||
|
||||
+98
-225
@@ -1,52 +1,56 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-08-14T00:00:00Z",
|
||||
"updated_at": "2026-07-17T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.community.json",
|
||||
"presets": {
|
||||
"a11y-governance": {
|
||||
"name": "A11Y Governance",
|
||||
"id": "a11y-governance",
|
||||
"version": "0.4.3",
|
||||
"description": "Adds WCAG 2.2 AA, accessible status output, bilingual CEFR-B2 delivery, inclusive-content and didactic-comment governance, and provider-neutral model routing.",
|
||||
"version": "0.4.0",
|
||||
"description": "Adds accessibility (WCAG 2.2 AA), bilingual DE/EN delivery, CEFR-B2 readability, inclusive-content governance, didactic inline-code-comment review, and audit-ready Spec Kit run evidence.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-a11y-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-a11y-governance/archive/refs/tags/v0.4.3.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-a11y-governance/archive/refs/tags/v0.4.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-a11y-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-a11y-governance/blob/v0.4.3/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-a11y-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 11,
|
||||
"templates": 10,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
"a11y",
|
||||
"accessibility",
|
||||
"bilingual",
|
||||
"wcag",
|
||||
"wcag-2-2",
|
||||
"cefr-b2",
|
||||
"inclusion",
|
||||
"model-routing"
|
||||
"include-everyone",
|
||||
"didactic-comments"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-12T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"agent-parity-governance": {
|
||||
"name": "Agent Parity Governance",
|
||||
"id": "agent-parity-governance",
|
||||
"version": "0.4.2",
|
||||
"description": "Adds shared-guidance parity, fleet-completion evidence, secret-free runner metadata, audit-ready Spec Kit evidence, and agent-neutral model routing across declared AI-agent surfaces.",
|
||||
"version": "0.3.0",
|
||||
"description": "Adds shared-guidance parity, audit-ready Spec-Kit run evidence, and agent-neutral model-routing guidance across a project's declared AI-agent instruction surfaces so agent guidance does not drift.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/archive/refs/tags/v0.4.2.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/archive/refs/tags/v0.3.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/blob/v0.4.2/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-agent-parity-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 7,
|
||||
"templates": 6,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
@@ -59,13 +63,13 @@
|
||||
"multi-agent"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-13T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"aide-in-place": {
|
||||
"name": "AIDE In-Place Migration",
|
||||
"id": "aide-in-place",
|
||||
"version": "1.0.0",
|
||||
"description": "Adapts the AIDE workflow for in-place technology migrations (X \u2192 Y pattern). Overrides vision, roadmap, progress, and work item commands with migration-specific guidance.",
|
||||
"description": "Adapts the AIDE workflow for in-place technology migrations (X → Y pattern). Overrides vision, roadmap, progress, and work item commands with migration-specific guidance.",
|
||||
"author": "mnriem",
|
||||
"repository": "https://github.com/mnriem/spec-kit-presets",
|
||||
"download_url": "https://github.com/mnriem/spec-kit-presets/releases/download/aide-in-place-v1.0.0/aide-in-place.zip",
|
||||
@@ -92,41 +96,51 @@
|
||||
"architecture-governance": {
|
||||
"name": "Architecture Governance",
|
||||
"id": "architecture-governance",
|
||||
"version": "0.5.2",
|
||||
"description": "Adds secure architecture, STRIDE/CAPEC threat modeling, arc42/S-ADR guidance, Zero Trust, SAMM, BSI cloud assurance, audit evidence, and provider-neutral model routing.",
|
||||
"version": "0.5.0",
|
||||
"description": "Adds secure software architecture, STRIDE+CAPEC threat modeling, arc42 security cross-cutting concepts, S-ADRs, Zero Trust applicability, OWASP SAMM governance, BSI C3A cloud autonomy, BSI C5 cloud compliance assurance, and audit-ready Spec Kit run evidence.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-architecture-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-architecture-governance/archive/refs/tags/v0.5.2.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-architecture-governance/archive/refs/tags/v0.5.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-architecture-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-architecture-governance/blob/v0.5.2/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-architecture-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 14,
|
||||
"templates": 13,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
"architecture",
|
||||
"governance",
|
||||
"threat-modeling",
|
||||
"stride",
|
||||
"capec",
|
||||
"arc42",
|
||||
"adr",
|
||||
"zero-trust",
|
||||
"samm",
|
||||
"isaqb",
|
||||
"cloud",
|
||||
"model-routing"
|
||||
"sovereignty",
|
||||
"c3a",
|
||||
"c5",
|
||||
"assurance"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-11T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"autonomous-run-governance": {
|
||||
"name": "Autonomous Run Governance",
|
||||
"id": "autonomous-run-governance",
|
||||
"version": "0.3.3",
|
||||
"description": "Adds permission-bounded autonomous delivery, an optional intake-review gate, and preservation of the project's learner and accessibility contract.",
|
||||
"version": "0.2.2",
|
||||
"description": "Adds permission-bounded, evidence-first governance for autonomous Spec Kit delivery with validated status, stop, resume, exact-head proof, closeout, and learner guidance.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/archive/refs/tags/v0.3.3.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/archive/refs/tags/v0.2.2.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/blob/v0.3.3/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-autonomous-run-governance/blob/v0.2.2/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
@@ -141,10 +155,10 @@
|
||||
"governance",
|
||||
"evidence",
|
||||
"permissions",
|
||||
"accessibility"
|
||||
"resume"
|
||||
],
|
||||
"created_at": "2026-07-13T00:00:00Z",
|
||||
"updated_at": "2026-07-28T00:00:00Z"
|
||||
"updated_at": "2026-07-17T00:00:00Z"
|
||||
},
|
||||
"canon-core": {
|
||||
"name": "Canon Core",
|
||||
@@ -228,19 +242,19 @@
|
||||
"cross-platform-governance": {
|
||||
"name": "Cross-Platform Governance",
|
||||
"id": "cross-platform-governance",
|
||||
"version": "0.2.2",
|
||||
"description": "Adds Bash/PowerShell parity, read-only checks, path and native-override review, Unix man pages, bilingual PowerShell help, and provider-neutral model routing.",
|
||||
"version": "0.2.0",
|
||||
"description": "Adds Bash + PowerShell parity, Unix man-pages, bilingual comment-based help, Verb-Noun Cmdlet discipline, and audit-ready Spec Kit run evidence for scripting projects managed with Spec Kit.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance/archive/refs/tags/v0.2.2.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance/archive/refs/tags/v0.2.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance/blob/v0.2.2/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-cross-platform-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 9,
|
||||
"templates": 8,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
@@ -248,10 +262,15 @@
|
||||
"governance",
|
||||
"bash",
|
||||
"powershell",
|
||||
"model-routing"
|
||||
"man-page",
|
||||
"cmdlet",
|
||||
"verb-noun",
|
||||
"windows",
|
||||
"macos",
|
||||
"linux"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-12T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"explicit-task-dependencies": {
|
||||
"name": "Explicit Task Dependencies",
|
||||
@@ -344,120 +363,37 @@
|
||||
"created_at": "2026-05-05T08:00:00Z",
|
||||
"updated_at": "2026-06-22T00:00:00Z"
|
||||
},
|
||||
"intake-authoring-governance": {
|
||||
"name": "Intake Authoring Governance",
|
||||
"id": "intake-authoring-governance",
|
||||
"version": "0.3.1",
|
||||
"description": "Governs traceable intake CRUD, language-aware requirements collections, bounded public HTTPS sources, and explicitly approved single or series authoring.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-intake-authoring-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-intake-authoring-governance/archive/refs/tags/v0.3.1.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-intake-authoring-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-intake-authoring-governance/blob/v0.3.1/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 13,
|
||||
"commands": 5,
|
||||
"scripts": 7
|
||||
},
|
||||
"tags": [
|
||||
"intake",
|
||||
"authoring",
|
||||
"governance",
|
||||
"requirements",
|
||||
"migration"
|
||||
],
|
||||
"created_at": "2026-07-22T00:00:00Z",
|
||||
"updated_at": "2026-08-14T00:00:00Z"
|
||||
},
|
||||
"intake-review-governance": {
|
||||
"name": "Intake Review Governance",
|
||||
"id": "intake-review-governance",
|
||||
"version": "0.2.0",
|
||||
"description": "Reviews single, series, campaign, and language-aware requirements collections before Spec Kit execution.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-intake-review-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-intake-review-governance/archive/refs/tags/v0.2.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-intake-review-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-intake-review-governance/blob/v0.2.0/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 8,
|
||||
"commands": 3,
|
||||
"scripts": 4
|
||||
},
|
||||
"tags": [
|
||||
"intake",
|
||||
"review",
|
||||
"governance",
|
||||
"requirements",
|
||||
"quality-gate"
|
||||
],
|
||||
"created_at": "2026-07-21T00:00:00Z",
|
||||
"updated_at": "2026-07-28T00:00:00Z"
|
||||
},
|
||||
"intake-sequencing-governance": {
|
||||
"name": "Intake Sequencing Governance",
|
||||
"id": "intake-sequencing-governance",
|
||||
"version": "0.2.2",
|
||||
"description": "Manages language-aware intake-series order, typed dependencies, lifecycle, and authority-neutral next-candidate selection.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-intake-sequencing-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-intake-sequencing-governance/archive/refs/tags/v0.2.2.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-intake-sequencing-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-intake-sequencing-governance/blob/v0.2.2/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 11,
|
||||
"commands": 6,
|
||||
"scripts": 8
|
||||
},
|
||||
"tags": [
|
||||
"intake",
|
||||
"sequencing",
|
||||
"governance",
|
||||
"dag",
|
||||
"lifecycle"
|
||||
],
|
||||
"created_at": "2026-07-27T00:00:00Z",
|
||||
"updated_at": "2026-07-28T00:00:00Z"
|
||||
},
|
||||
"isaqb-architecture-governance": {
|
||||
"name": "iSAQB Architecture Governance",
|
||||
"id": "isaqb-architecture-governance",
|
||||
"version": "0.2.2",
|
||||
"description": "Adds iSAQB/CPSA-F and arc42 architecture governance, architecture views, quality scenarios, ADRs, risks, technical-debt evidence, and provider-neutral model routing.",
|
||||
"version": "0.2.0",
|
||||
"description": "Adds general iSAQB/CPSA-F and arc42 software-architecture governance, including audit-ready Spec Kit run evidence for architecture goals, views, quality scenarios, ADRs, risks, and technical debt.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/archive/refs/tags/v0.2.2.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/archive/refs/tags/v0.2.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/blob/v0.2.2/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 14,
|
||||
"templates": 13,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
"architecture",
|
||||
"governance",
|
||||
"isaqb",
|
||||
"cpsa-f",
|
||||
"arc42",
|
||||
"model-routing"
|
||||
"adr",
|
||||
"quality-attributes",
|
||||
"architecture-views",
|
||||
"technical-debt"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-11T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"jira": {
|
||||
"name": "Jira Issue Tracking",
|
||||
@@ -517,35 +453,6 @@
|
||||
"created_at": "2026-05-08T00:00:00Z",
|
||||
"updated_at": "2026-05-08T00:00:00Z"
|
||||
},
|
||||
"model-routing-governance": {
|
||||
"name": "Model Routing Governance",
|
||||
"id": "model-routing-governance",
|
||||
"version": "0.1.4",
|
||||
"description": "Maps provider-neutral Spec Kit roles to validated harness-local runner profiles without storing model availability, credentials, or machine-specific selections in Git.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-model-routing-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-model-routing-governance/archive/refs/tags/v0.1.4.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-model-routing-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-model-routing-governance/blob/v0.1.4/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 4,
|
||||
"commands": 2,
|
||||
"scripts": 2
|
||||
},
|
||||
"tags": [
|
||||
"model-routing",
|
||||
"agents",
|
||||
"governance",
|
||||
"provider-neutral",
|
||||
"cross-platform"
|
||||
],
|
||||
"created_at": "2026-08-10T00:00:00Z",
|
||||
"updated_at": "2026-08-10T00:00:00Z"
|
||||
},
|
||||
"multi-repo-branching": {
|
||||
"name": "Multi-Repo Branching",
|
||||
"id": "multi-repo-branching",
|
||||
@@ -573,35 +480,6 @@
|
||||
"created_at": "2026-04-09T00:00:00Z",
|
||||
"updated_at": "2026-04-09T00:00:00Z"
|
||||
},
|
||||
"parallel-autonomous-run-governance": {
|
||||
"name": "Parallel Autonomous Run Governance",
|
||||
"id": "parallel-autonomous-run-governance",
|
||||
"version": "0.2.4",
|
||||
"description": "Coordinates permission-bounded autonomous campaigns while preserving the project's learner and accessibility contract across workers and consolidation.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance/archive/refs/tags/v0.2.4.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-parallel-autonomous-run-governance/blob/v0.2.4/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.3"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 9,
|
||||
"commands": 5,
|
||||
"scripts": 2
|
||||
},
|
||||
"tags": [
|
||||
"parallel",
|
||||
"autonomous",
|
||||
"governance",
|
||||
"accessibility",
|
||||
"orchestration"
|
||||
],
|
||||
"created_at": "2026-07-22T00:00:00Z",
|
||||
"updated_at": "2026-07-28T00:00:00Z"
|
||||
},
|
||||
"pirate": {
|
||||
"name": "Pirate Speak (Full)",
|
||||
"id": "pirate",
|
||||
@@ -631,7 +509,7 @@
|
||||
"name": "Screenwriting",
|
||||
"id": "screenwriting",
|
||||
"version": "1.0.0",
|
||||
"description": "Spec-Driven Development for screenwriting/scriptwriting/tutorials: feature films, television (pilot, episode, limited series), and stage plays. Adapts the Spec Kit workflow to screenplay craft \u2014 slug lines, action lines, act breaks, beat sheets, and industry-standard pitch documents replace prose fiction conventions. Supports three-act, Save the Cat, TV pilot, network episode, cable/streaming episode, and stage-play structural frameworks.",
|
||||
"description": "Spec-Driven Development for screenwriting/scriptwriting/tutorials: feature films, television (pilot, episode, limited series), and stage plays. Adapts the Spec Kit workflow to screenplay craft — slug lines, action lines, act breaks, beat sheets, and industry-standard pitch documents replace prose fiction conventions. Supports three-act, Save the Cat, TV pilot, network episode, cable/streaming episode, and stage-play structural frameworks.",
|
||||
"author": "Andreas Daumann",
|
||||
"repository": "https://github.com/adaumann/speckit-preset-screenwriting",
|
||||
"download_url": "https://github.com/adaumann/speckit-preset-screenwriting/archive/refs/tags/v1.0.0.zip",
|
||||
@@ -668,30 +546,52 @@
|
||||
"security-governance": {
|
||||
"name": "Security Governance",
|
||||
"id": "security-governance",
|
||||
"version": "0.6.2",
|
||||
"description": "Adds memory-safe-language and secure-coding governance, exact-head security evidence, ASVS, supply-chain transparency, EU regulatory screening, and provider-neutral model routing.",
|
||||
"version": "0.6.0",
|
||||
"description": "Adds memory-safe-language preference, language-specific secure coding profiles, audit-ready Spec-Kit run evidence, ASVS verification, SBOM/AI-SBOM supply-chain transparency, CRA awareness, and regulatory applicability screening for NIS2, CRA, EU AI Act, and DORA to Spec Kit.",
|
||||
"author": "Thorsten Hindermann",
|
||||
"repository": "https://github.com/hindermath/spec-kit-preset-security-governance",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-security-governance/archive/refs/tags/v0.6.2.zip",
|
||||
"download_url": "https://github.com/hindermath/spec-kit-preset-security-governance/archive/refs/tags/v0.6.0.zip",
|
||||
"homepage": "https://github.com/hindermath/spec-kit-preset-security-governance",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-security-governance/blob/v0.6.2/README.md",
|
||||
"documentation": "https://github.com/hindermath/spec-kit-preset-security-governance/blob/main/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.8.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 15,
|
||||
"templates": 14,
|
||||
"commands": 3
|
||||
},
|
||||
"tags": [
|
||||
"security",
|
||||
"governance",
|
||||
"secure-coding",
|
||||
"msl",
|
||||
"ssdf",
|
||||
"asvs",
|
||||
"supply-chain",
|
||||
"model-routing"
|
||||
"sbom",
|
||||
"ai-sbom",
|
||||
"vex",
|
||||
"slsa",
|
||||
"cwe-top-25",
|
||||
"secure-coding",
|
||||
"rust",
|
||||
"go",
|
||||
"swift",
|
||||
"java",
|
||||
"kotlin",
|
||||
"python",
|
||||
"typescript",
|
||||
"g7",
|
||||
"bsi",
|
||||
"cra",
|
||||
"cyber-resilience-act",
|
||||
"nis2",
|
||||
"ai-act",
|
||||
"dora",
|
||||
"regulatory"
|
||||
],
|
||||
"created_at": "2026-04-27T00:00:00Z",
|
||||
"updated_at": "2026-08-10T00:00:00Z"
|
||||
"updated_at": "2026-06-14T00:00:00Z"
|
||||
},
|
||||
"sicario-core": {
|
||||
"name": "SicarioSpec Core",
|
||||
@@ -724,7 +624,7 @@
|
||||
"name": "Spec2Cloud",
|
||||
"id": "spec2cloud",
|
||||
"version": "1.1.0",
|
||||
"description": "Spec-driven workflow tuned for shipping to Azure: spec \u2192 plan \u2192 tasks \u2192 implement \u2192 deploy.",
|
||||
"description": "Spec-driven workflow tuned for shipping to Azure: spec → plan → tasks → implement → deploy.",
|
||||
"author": "Azure Samples",
|
||||
"repository": "https://github.com/Azure-Samples/Spec2Cloud",
|
||||
"download_url": "https://github.com/Azure-Samples/Spec2Cloud/releases/download/spec-kit-spec2cloud-v1.1.0/preset.zip",
|
||||
@@ -747,39 +647,12 @@
|
||||
"created_at": "2026-04-30T00:00:00Z",
|
||||
"updated_at": "2026-04-30T00:00:00Z"
|
||||
},
|
||||
"specassay": {
|
||||
"name": "SpecAssay",
|
||||
"id": "specassay",
|
||||
"version": "0.3.4",
|
||||
"description": "Appends durable-ID, Carries, and SpecAssay vocabulary onto Spec Kit spec, tasks, and constitution templates.",
|
||||
"author": "Rik Dryfoos",
|
||||
"repository": "https://github.com/rdryfoos/specassay",
|
||||
"download_url": "https://github.com/rdryfoos/specassay/releases/download/v0.3.4/specassay-preset-0.3.4.zip",
|
||||
"homepage": "https://github.com/rdryfoos/specassay",
|
||||
"documentation": "https://github.com/rdryfoos/specassay/blob/main/presets/specassay/README.md",
|
||||
"license": "MIT",
|
||||
"requires": {
|
||||
"speckit_version": ">=0.14.0"
|
||||
},
|
||||
"provides": {
|
||||
"templates": 3,
|
||||
"commands": 0
|
||||
},
|
||||
"tags": [
|
||||
"traceability",
|
||||
"durable-ids",
|
||||
"governance",
|
||||
"sdd"
|
||||
],
|
||||
"created_at": "2026-08-14T00:00:00Z",
|
||||
"updated_at": "2026-08-14T00:00:00Z"
|
||||
},
|
||||
"test-first-governance": {
|
||||
"name": "Test-First Governance",
|
||||
"id": "test-first-governance",
|
||||
"version": "1.3.0",
|
||||
"description": "Governs TDD with coverage-complete BDD/ATDD Gherkin scenarios, explicit suite ownership, professional test reports, traceability, and risk-based quality gates.",
|
||||
"author": "Zolt\u00e1n Katona, PhD",
|
||||
"author": "Zoltán Katona, PhD",
|
||||
"repository": "https://github.com/ka-zo/spec-kit-preset-test-first-governance",
|
||||
"download_url": "https://github.com/ka-zo/spec-kit-preset-test-first-governance/archive/refs/tags/1.3.0.zip",
|
||||
"homepage": "https://github.com/ka-zo/spec-kit-preset-test-first-governance",
|
||||
|
||||
+1
-24
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"updated_at": "2026-08-04T00:00:00Z",
|
||||
"updated_at": "2026-04-24T00:00:00Z",
|
||||
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.json",
|
||||
"presets": {
|
||||
"lean": {
|
||||
@@ -25,29 +25,6 @@
|
||||
"workflow",
|
||||
"core"
|
||||
]
|
||||
},
|
||||
"constitution-sync": {
|
||||
"name": "Constitution Template Sync",
|
||||
"id": "constitution-sync",
|
||||
"version": "1.0.0",
|
||||
"description": "Opt-in: restores guarded install-time constitution seeding and /constitution propagation for teams that treat materialized templates as reviewed artifacts.",
|
||||
"author": "github",
|
||||
"repository": "https://github.com/github/spec-kit",
|
||||
"license": "MIT",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"speckit_version": ">=0.14.4"
|
||||
},
|
||||
"provides": {
|
||||
"commands": 1,
|
||||
"templates": 0
|
||||
},
|
||||
"tags": [
|
||||
"constitution",
|
||||
"governance",
|
||||
"templates",
|
||||
"compatibility"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# Constitution Template Sync
|
||||
|
||||
An **opt-in** preset that restores materialized constitution workflows. It refreshes an unchanged
|
||||
generated `.specify/memory/constitution.md` when constitution-providing presets are installed,
|
||||
removed, enabled, disabled, or reprioritized. After `/constitution` updates the live file, it also
|
||||
aligns `plan-template.md`, `spec-template.md`, `tasks-template.md`, project-local command files, and
|
||||
guidance docs so they reflect the current principles.
|
||||
|
||||
This propagation used to be built into `/constitution`; it was dropped when the command moved to the
|
||||
preset model. Installing this preset opts you back into materialization: preset stack changes refresh
|
||||
the generated constitution, and `/constitution` propagates its guidance into reviewed, committed
|
||||
artifacts instead of relying on runtime resolution alone.
|
||||
|
||||
> **What you're opting into.** Propagation was removed deliberately — it duplicates the constitution
|
||||
> as the source of truth and can fight the composition stack (materialized edits get shadowed or
|
||||
> clobbered on the next recompose). This preset knowingly **reintroduces** that behavior, and those
|
||||
> tradeoffs, for teams that want it. Read the [caveats](#caveats-you-take-on) before installing.
|
||||
|
||||
For most projects the default composable stack is the **recommended** approach, and at organization
|
||||
scale it is usually the stronger governance model. Runtime resolution keeps the live constitution as
|
||||
the single source of truth (nothing to re-sync, so nothing drifts), and the stack composes the
|
||||
**entire** Spec Kit ecosystem — not just the SDD commands, but every command, template, script and
|
||||
extension — with explicit priority levels, strategies, and independent versioning. It is a
|
||||
capability, not automatic governance: a core team authors its own organizational presets and
|
||||
extensions, then owns, versions, and audits that policy in one place and rolls it across many
|
||||
repositories, instead of scattering frozen, per-repo copies no central team can see. This preset is
|
||||
a supported escape hatch for teams whose workflow depends on reviewing materialized artifacts
|
||||
directly — useful as a bridge, though for org-wide policy the better long-term path is usually a
|
||||
versioned preset a core team maintains.
|
||||
|
||||
## What it does
|
||||
|
||||
Its presence enables core's guarded install-time constitution reconciliation. Installing the preset
|
||||
materializes the currently resolved `constitution-template`; later stack changes re-materialize it
|
||||
only while the live file still matches its recorded generated-content hash. Human edits disable
|
||||
automatic replacement.
|
||||
|
||||
It also ships a single `wrap`-strategy override of `speckit.constitution`. It composes on top of the
|
||||
current core command (via `{CORE_TEMPLATE}`), so it stays forward-compatible with core changes, and
|
||||
appends a propagation pass that, after the constitution is written:
|
||||
|
||||
- Aligns `plan/spec/tasks-template.md` in `.specify/templates/` with the updated principles.
|
||||
- Updates **project-local** command files and guidance docs to correct stale references.
|
||||
- Extends the Sync Impact Report in `.specify/memory/constitution.md` with the files it touched.
|
||||
|
||||
## What it does not do
|
||||
|
||||
- It does **not** change behavior for anyone who does not install it — the default runtime
|
||||
resolution model is untouched.
|
||||
- It does **not** disable runtime resolution. `plan`, `tasks`, and `analyze` still read the live
|
||||
constitution every run; this preset adds materialized copies on top — it does not replace the
|
||||
source of truth.
|
||||
- It does **not** overwrite an authored or edited constitution. Install-time reconciliation only
|
||||
replaces content whose provenance proves it is an unchanged generated file.
|
||||
- It does **not** edit versioned, package-owned files — templates or command files provided or
|
||||
wrapped by another preset or extension. Those are recomposed from the resolution stack, so it
|
||||
only ever writes into your project's own `.specify/templates/` scaffolds and command files that
|
||||
are not managed by a preset/extension.
|
||||
|
||||
## When to use it
|
||||
|
||||
Install it **only** if your team treats the materialized templates and commands as
|
||||
**reviewed, committed artifacts** — for example, if `plan-template.md`'s Constitution Check is
|
||||
read in PRs as "here are our current gates" and is expected to track the constitution.
|
||||
|
||||
If you rely on the default runtime-resolution model, you do **not** need this preset: the live
|
||||
constitution is already the single source of truth and there is nothing to sync.
|
||||
|
||||
## Caveats you take on
|
||||
|
||||
The preset resolution stack is how Spec Kit composes templates and commands going forward: they are
|
||||
**layered, package-owned artifacts recomposed on demand**, not frozen files you edit in place.
|
||||
Propagation is the opposite idea — it **materializes** guidance into files and freezes it. That
|
||||
tension is the main thing to understand before installing:
|
||||
|
||||
- **Materialized copies can drift.** Anything propagated is a snapshot; if you amend the
|
||||
constitution and do not re-run `/constitution`, the copies fall out of sync. The default runtime
|
||||
model has no drift because it reads the live constitution every run.
|
||||
|
||||
- **Edits to composed files do not survive reconciliation.** If the rest of your SDD flow is
|
||||
preset/extension-managed, the commands it materializes (`speckit.plan`, `speckit.specify`,
|
||||
`speckit.tasks`, `speckit.analyze`, `speckit.implement`, …) are recomputed from the stack. Any
|
||||
guidance propagated into them is clobbered the next time the stack reconciles — on
|
||||
`specify integration use <key>` / `switch`, `specify integration upgrade`, or any preset/extension
|
||||
install or remove. The same applies to templates owned by another preset/extension. This is why
|
||||
the preset restricts itself to project-local files; propagation is reliable **only** for
|
||||
artifacts you own outright.
|
||||
|
||||
- **A pre-filled Constitution Check can bias `/plan`.** Materializing concrete gates into
|
||||
`plan-template.md` replaces the runtime pointer, so the first `/plan` pass may anchor on the
|
||||
frozen text. Keep the pointer unless you specifically want committed gates.
|
||||
|
||||
**Bottom line:** this preset fits projects whose governed templates and commands are project-local
|
||||
artifacts they review, with the rest of the SDD flow on the plain bundled core. If your
|
||||
`plan`/`specify`/`tasks`/`analyze` commands or templates come from other presets or extensions,
|
||||
prefer the default runtime-resolution model.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# constitution-sync is a bundled preset — no download needed
|
||||
specify preset add constitution-sync
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Test from local directory
|
||||
specify preset add --dev ./presets/constitution-sync
|
||||
|
||||
# Verify the wrapped command resolves
|
||||
specify preset resolve speckit.constitution
|
||||
|
||||
# Remove when done
|
||||
specify preset remove constitution-sync
|
||||
```
|
||||
|
||||
## Migrating back to the default
|
||||
|
||||
To move back to runtime resolution, reset each materialized `## Constitution Check` section in
|
||||
`.specify/templates/plan-template.md` to the pointer:
|
||||
|
||||
```text
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
[Gates determined based on constitution file]
|
||||
```
|
||||
|
||||
Then remove this preset. See `docs/upgrade.md` for details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
description: Create or update the project constitution, then propagate the amended guidance into dependent templates and installed command files (opt-in template sync).
|
||||
strategy: wrap
|
||||
handoffs:
|
||||
- label: Build Specification
|
||||
agent: speckit.specify
|
||||
prompt: Implement the feature specification based on the updated constitution. I want to build...
|
||||
---
|
||||
|
||||
{CORE_TEMPLATE}
|
||||
|
||||
## Constitution Template Sync
|
||||
|
||||
> **This section supersedes the "Scope Guard" above for template and command propagation.**
|
||||
> The core Scope Guard states that dependent templates and commands "are not modified here" —
|
||||
> that restriction is intentionally lifted by this preset. Propagating amended guidance into the
|
||||
> project's own dependent scaffolds and installed command files (as described below) **is in
|
||||
> scope** whenever this preset is installed. The Scope Guard's other constraints still apply:
|
||||
> do not implement features, generate application code, or touch artifacts unrelated to the
|
||||
> constitution/template workflow.
|
||||
|
||||
After you have written the updated constitution above, perform a consistency propagation pass
|
||||
so the dependent artifacts reflect the amended principles:
|
||||
|
||||
1. Read `.specify/templates/plan-template.md` and ensure any "Constitution Check" or rules align
|
||||
with the updated principles. Only materialize concrete gate text here if your team intends to
|
||||
review it as committed content; otherwise leave the runtime pointer
|
||||
`[Gates determined based on constitution file]` in place so `/plan` fills it from the live
|
||||
constitution.
|
||||
2. Read `.specify/templates/spec-template.md` for scope/requirements alignment — update if the
|
||||
constitution adds/removes mandatory sections or constraints.
|
||||
3. Read `.specify/templates/tasks-template.md` and ensure task categorization reflects new or
|
||||
removed principle-driven task types (e.g., observability, versioning, testing discipline).
|
||||
4. Read each installed Spec Kit command file for your agent (including this one) — named
|
||||
`speckit.*` or `speckit-*` (dot or hyphen depending on the agent), or laid out as
|
||||
`speckit-<name>/SKILL.md` for skills-based integrations, e.g. in `.github/agents/`,
|
||||
`.github/skills/`, `.claude/skills/`, or your agent's equivalent commands directory — to verify
|
||||
no outdated references (CLAUDE-only or other agent-specific names) remain when generic guidance
|
||||
is required. **Only hand-edit a command file if it is a project-local file not managed by a
|
||||
preset or extension.** Command files that are composed from the resolution stack (anything
|
||||
provided or wrapped by a preset/extension) must be regenerated through the stack — do **not**
|
||||
edit them in place, because reconciliation (`specify integration use`, `specify integration
|
||||
upgrade`, or any preset/extension install/remove) will clobber the edits.
|
||||
5. Read any runtime guidance docs (e.g., `README.md`, `docs/quickstart.md`, or agent-specific
|
||||
guidance files if present) and update references to principles that changed.
|
||||
|
||||
Then extend the Sync Impact Report at the top of `.specify/memory/constitution.md` with:
|
||||
|
||||
- Templates requiring updates (✅ updated / ⚠ pending) with file paths.
|
||||
|
||||
**Do not edit versioned preset- or extension-provided template or command files directly.** Those
|
||||
artifacts are owned by their packages and are recomposed on the package's next update or on stack
|
||||
reconciliation — hand edits are clobbered. Limit propagation to the project's own
|
||||
`.specify/templates/` scaffolds and to command files that are not managed by a preset or extension.
|
||||
@@ -1,30 +0,0 @@
|
||||
schema_version: "1.0"
|
||||
|
||||
preset:
|
||||
id: "constitution-sync"
|
||||
name: "Constitution Template Sync"
|
||||
version: "1.0.0"
|
||||
description: "Opt-in: restores guarded install-time constitution seeding and /constitution propagation for teams that treat materialized templates as reviewed artifacts."
|
||||
author: "github"
|
||||
repository: "https://github.com/github/spec-kit"
|
||||
license: "MIT"
|
||||
|
||||
requires:
|
||||
# Requires the runtime-resolution baseline (#3790, shipped in 0.14.4) where the
|
||||
# core /constitution command no longer propagates. Installing this preset on an
|
||||
# older core would double-apply propagation.
|
||||
speckit_version: ">=0.14.4"
|
||||
|
||||
provides:
|
||||
templates:
|
||||
- type: "command"
|
||||
name: "speckit.constitution"
|
||||
file: "commands/speckit.constitution.md"
|
||||
description: "Wrap /constitution to also propagate guidance into dependent templates and command files"
|
||||
strategy: "wrap"
|
||||
|
||||
tags:
|
||||
- "constitution"
|
||||
- "governance"
|
||||
- "templates"
|
||||
- "compatibility"
|
||||
@@ -8,24 +8,6 @@ description: Create or update the project constitution.
|
||||
$ARGUMENTS
|
||||
```
|
||||
|
||||
## Scope Guard
|
||||
|
||||
This command's own work is limited to creating or updating the project constitution and
|
||||
propagating constitution-driven changes to dependent Spec Kit artifacts.
|
||||
|
||||
- Classify every part of the user input as constitution content or a separate non-governance
|
||||
intent. Feature implementation, code generation, refactoring, build, and deployment requests
|
||||
are examples of non-governance intents.
|
||||
- You **MUST NOT** execute any non-governance intent. Defer each one to `Next Actions`.
|
||||
- You **MUST NOT** create, modify, or delete application source files or other artifacts
|
||||
unrelated to the constitution workflow.
|
||||
- If an instruction could be either constitution content or a non-governance intent, ask for
|
||||
clarification before making changes.
|
||||
- After updating the constitution, list each deferred intent in a `Next Actions` section with an
|
||||
appropriate follow-up Spec Kit command, such as `__SPECKIT_COMMAND_SPECIFY__`, but do not
|
||||
invoke it.
|
||||
- Omit `Next Actions` when there are no non-governance intents.
|
||||
|
||||
## Outline
|
||||
|
||||
1. Create or update the project constitution and store it in `.specify/memory/constitution.md`.
|
||||
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "specify-cli"
|
||||
version = "0.16.5.dev0"
|
||||
version = "0.13.2"
|
||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
@@ -39,7 +39,6 @@ packages = ["src/specify_cli"]
|
||||
"templates/commands" = "specify_cli/core_pack/commands"
|
||||
"scripts/bash" = "specify_cli/core_pack/scripts/bash"
|
||||
"scripts/powershell" = "specify_cli/core_pack/scripts/powershell"
|
||||
"scripts/python" = "specify_cli/core_pack/scripts/python"
|
||||
# Bundled extensions (installable via `specify extension add <name>`)
|
||||
"extensions/git" = "specify_cli/core_pack/extensions/git"
|
||||
"extensions/agent-context" = "specify_cli/core_pack/extensions/agent-context"
|
||||
@@ -49,7 +48,6 @@ packages = ["src/specify_cli"]
|
||||
"workflows/speckit" = "specify_cli/core_pack/workflows/speckit"
|
||||
# Bundled presets (installable via `specify preset add <name>` or `specify init --preset <name>`)
|
||||
"presets/lean" = "specify_cli/core_pack/presets/lean"
|
||||
"presets/constitution-sync" = "specify_cli/core_pack/presets/constitution-sync"
|
||||
# Community bundle catalog snapshot (used for offline discovery)
|
||||
"bundles/catalog.community.json" = "specify_cli/core_pack/bundles/catalog.community.json"
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
# --require-tasks Require tasks.md to exist (for implementation phase)
|
||||
# --include-tasks Include tasks.md in AVAILABLE_DOCS list
|
||||
# --paths-only Only output path variables (no validation)
|
||||
# --template NAME Include composed template content in JSON output
|
||||
# --help, -h Show help message
|
||||
#
|
||||
# OUTPUTS:
|
||||
@@ -27,10 +26,9 @@ JSON_MODE=false
|
||||
REQUIRE_TASKS=false
|
||||
INCLUDE_TASKS=false
|
||||
PATHS_ONLY=false
|
||||
TEMPLATE_NAME=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--json)
|
||||
JSON_MODE=true
|
||||
;;
|
||||
@@ -43,14 +41,6 @@ while [[ $# -gt 0 ]]; do
|
||||
--paths-only)
|
||||
PATHS_ONLY=true
|
||||
;;
|
||||
--template)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "ERROR: --template requires a template name" >&2
|
||||
exit 1
|
||||
fi
|
||||
TEMPLATE_NAME="$1"
|
||||
;;
|
||||
--help|-h)
|
||||
cat << 'EOF'
|
||||
Usage: check-prerequisites.sh [OPTIONS]
|
||||
@@ -62,7 +52,6 @@ OPTIONS:
|
||||
--require-tasks Require tasks.md to exist (for implementation phase)
|
||||
--include-tasks Include tasks.md in AVAILABLE_DOCS list
|
||||
--paths-only Only output path variables (no prerequisite validation)
|
||||
--template NAME Include composed template content in JSON output
|
||||
--help, -h Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
@@ -79,11 +68,10 @@ EOF
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown option '$1'. Use --help for usage information." >&2
|
||||
echo "ERROR: Unknown option '$arg'. Use --help for usage information." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Source common functions
|
||||
@@ -168,16 +156,6 @@ if $INCLUDE_TASKS && [[ -f "$TASKS" ]]; then
|
||||
docs+=("tasks.md")
|
||||
fi
|
||||
|
||||
TEMPLATE_CONTENT=""
|
||||
if [[ -n "$TEMPLATE_NAME" ]]; then
|
||||
if TEMPLATE_CONTENT=$(resolve_template_content "$TEMPLATE_NAME" "$REPO_ROOT"; status=$?; printf x; exit "$status"); then
|
||||
TEMPLATE_CONTENT="${TEMPLATE_CONTENT%x}"
|
||||
else
|
||||
echo "ERROR: Could not resolve required $TEMPLATE_NAME from the template override stack for $REPO_ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Output results
|
||||
if $JSON_MODE; then
|
||||
# Build JSON array of documents
|
||||
@@ -187,18 +165,10 @@ if $JSON_MODE; then
|
||||
else
|
||||
json_docs=$(printf '%s\n' "${docs[@]}" | jq -R . | jq -s .)
|
||||
fi
|
||||
if [[ -n "$TEMPLATE_NAME" ]]; then
|
||||
jq -cn \
|
||||
--arg feature_dir "$FEATURE_DIR" \
|
||||
--argjson docs "$json_docs" \
|
||||
--arg template_content "$TEMPLATE_CONTENT" \
|
||||
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs,TEMPLATE_CONTENT:$template_content}'
|
||||
else
|
||||
jq -cn \
|
||||
--arg feature_dir "$FEATURE_DIR" \
|
||||
--argjson docs "$json_docs" \
|
||||
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs}'
|
||||
fi
|
||||
jq -cn \
|
||||
--arg feature_dir "$FEATURE_DIR" \
|
||||
--argjson docs "$json_docs" \
|
||||
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs}'
|
||||
else
|
||||
if [[ ${#docs[@]} -eq 0 ]]; then
|
||||
json_docs="[]"
|
||||
@@ -206,12 +176,7 @@ if $JSON_MODE; then
|
||||
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done)
|
||||
json_docs="[${json_docs%,}]"
|
||||
fi
|
||||
if [[ -n "$TEMPLATE_NAME" ]]; then
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"TEMPLATE_CONTENT":"%s"}\n' \
|
||||
"$(json_escape "$FEATURE_DIR")" "$json_docs" "$(json_escape "$TEMPLATE_CONTENT")"
|
||||
else
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
|
||||
fi
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
|
||||
fi
|
||||
else
|
||||
# Text output
|
||||
|
||||
+106
-320
@@ -398,101 +398,6 @@ json_escape() {
|
||||
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||
|
||||
_python3_command() {
|
||||
if command -v python3 >/dev/null 2>&1 &&
|
||||
python3 -c 'import sys; raise SystemExit(sys.version_info.major != 3)' >/dev/null 2>&1; then
|
||||
printf '%s\n' "python3"
|
||||
elif command -v python >/dev/null 2>&1 &&
|
||||
python -c 'import sys; raise SystemExit(sys.version_info.major != 3)' >/dev/null 2>&1; then
|
||||
printf '%s\n' "python"
|
||||
elif command -v py >/dev/null 2>&1 &&
|
||||
py -3 -c 'import sys' >/dev/null 2>&1; then
|
||||
printf '%s\n' "py -3"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
_sorted_extension_ids() {
|
||||
local ext_dir="$1"
|
||||
local python_spec
|
||||
if python_spec=$(_python3_command); then
|
||||
local -a python_cmd
|
||||
read -r -a python_cmd <<< "$python_spec"
|
||||
local py_stderr sorted_ids
|
||||
py_stderr=$(mktemp)
|
||||
if sorted_ids=$(SPECKIT_EXTENSIONS="$ext_dir" "${python_cmd[@]}" -c "
|
||||
import json, os, re, sys
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(os.environ['SPECKIT_EXTENSIONS'])
|
||||
registered = {}
|
||||
registry = root / '.registry'
|
||||
if os.path.lexists(registry):
|
||||
if not registry.is_file():
|
||||
print('registry_invalid: not a regular file', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
try:
|
||||
data = json.loads(registry.read_text(encoding='utf-8'))
|
||||
except Exception as exc:
|
||||
print('registry_invalid: ' + str(exc), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if not isinstance(data, dict):
|
||||
print('registry_invalid: root must be a mapping', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
raw_extensions = data.get('extensions', {})
|
||||
if not isinstance(raw_extensions, dict):
|
||||
print('registry_invalid: extensions must be a mapping', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
registered = raw_extensions
|
||||
|
||||
def priority(value):
|
||||
if isinstance(value, bool):
|
||||
return 10
|
||||
try:
|
||||
parsed = int(value)
|
||||
return parsed if parsed >= 1 else 10
|
||||
except (TypeError, ValueError, OverflowError):
|
||||
return 10
|
||||
|
||||
ranked = []
|
||||
for ext_id, meta in registered.items():
|
||||
if isinstance(ext_id, str) and re.fullmatch(r'[a-z0-9-]+', ext_id) and isinstance(meta, dict) and bool(meta.get('enabled', True)):
|
||||
ranked.append((priority(meta.get('priority')), ext_id))
|
||||
for path in root.iterdir():
|
||||
if path.is_dir() and re.fullmatch(r'[a-z0-9-]+', path.name) and path.name not in registered:
|
||||
ranked.append((10, path.name))
|
||||
for _, ext_id in sorted(ranked):
|
||||
print(ext_id)
|
||||
" 2>"$py_stderr"); then
|
||||
rm -f "$py_stderr"
|
||||
printf '%s\n' "$sorted_ids"
|
||||
return 0
|
||||
else
|
||||
echo "Error: invalid extension registry $ext_dir/.registry" >&2
|
||||
rm -f "$py_stderr"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -e "$ext_dir/.registry" ] || [ -L "$ext_dir/.registry" ]; then
|
||||
if [ ! -f "$ext_dir/.registry" ] || [ ! -r "$ext_dir/.registry" ]; then
|
||||
echo "Error: invalid extension registry $ext_dir/.registry" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Error: Python 3 is required to honor the extension registry" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
local ext extension_id
|
||||
for ext in "$ext_dir"/*/; do
|
||||
[ -d "$ext" ] || continue
|
||||
extension_id=$(basename "$ext")
|
||||
case "$extension_id" in *[!a-z0-9-]*) continue ;; esac
|
||||
printf '%s\n' "$extension_id"
|
||||
done
|
||||
}
|
||||
|
||||
# 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)
|
||||
@@ -503,8 +408,6 @@ resolve_template() {
|
||||
local repo_root="$2"
|
||||
local base="$repo_root/.specify/templates"
|
||||
|
||||
case "$template_name" in ""|*[!a-z0-9-]*) return 1 ;; esac
|
||||
|
||||
# Priority 1: Project overrides
|
||||
local override="$base/overrides/${template_name}.md"
|
||||
[ -f "$override" ] && echo "$override" && return 0
|
||||
@@ -513,32 +416,19 @@ resolve_template() {
|
||||
local presets_dir="$repo_root/.specify/presets"
|
||||
if [ -d "$presets_dir" ]; then
|
||||
local registry_file="$presets_dir/.registry"
|
||||
local python_spec=""
|
||||
local -a python_cmd=()
|
||||
if python_spec=$(_python3_command); then
|
||||
read -r -a python_cmd <<< "$python_spec"
|
||||
fi
|
||||
if [ -f "$registry_file" ] && [ "${#python_cmd[@]}" -gt 0 ]; then
|
||||
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
||||
# Read preset IDs sorted by priority (lower number = higher precedence).
|
||||
# The python3 call is wrapped in an if-condition so that set -e does not
|
||||
# abort the function when python3 exits non-zero (e.g. invalid JSON).
|
||||
local sorted_presets=""
|
||||
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" "${python_cmd[@]}" -c "
|
||||
import json, re, sys, os
|
||||
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
|
||||
import json, sys, os
|
||||
try:
|
||||
with open(os.environ['SPECKIT_REGISTRY'], encoding='utf-8') as f:
|
||||
with open(os.environ['SPECKIT_REGISTRY']) as f:
|
||||
data = json.load(f)
|
||||
presets = data.get('presets', {})
|
||||
def priority(meta):
|
||||
if not isinstance(meta, dict) or isinstance(meta.get('priority'), bool):
|
||||
return 10
|
||||
try:
|
||||
value = int(meta.get('priority', 10))
|
||||
return value if value >= 1 else 10
|
||||
except (TypeError, ValueError, OverflowError):
|
||||
return 10
|
||||
for pid, meta in sorted(presets.items(), key=lambda x: (priority(x[1]), x[0])):
|
||||
if isinstance(meta, dict) and bool(meta.get('enabled', True)) and re.fullmatch(r'[a-z0-9-]+', pid):
|
||||
for pid, meta in sorted(presets.items(), key=lambda x: x[1].get('priority', 10) if isinstance(x[1], dict) else 10):
|
||||
if isinstance(meta, dict) and meta.get('enabled', True) is not False:
|
||||
print(pid)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
@@ -548,8 +438,6 @@ except Exception:
|
||||
while IFS= read -r preset_id; do
|
||||
local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
candidate="$presets_dir/$preset_id/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done <<< "$sorted_presets"
|
||||
fi
|
||||
# python3 succeeded but registry has no presets — nothing to search
|
||||
@@ -559,8 +447,6 @@ except Exception:
|
||||
[ -d "$preset" ] || continue
|
||||
local candidate="$preset/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
candidate="$preset/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done
|
||||
fi
|
||||
else
|
||||
@@ -569,8 +455,6 @@ except Exception:
|
||||
[ -d "$preset" ] || continue
|
||||
local candidate="$preset/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
candidate="$preset/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done
|
||||
fi
|
||||
fi
|
||||
@@ -578,17 +462,13 @@ except Exception:
|
||||
# Priority 3: Extension-provided templates
|
||||
local ext_dir="$repo_root/.specify/extensions"
|
||||
if [ -d "$ext_dir" ]; then
|
||||
local sorted_extensions=""
|
||||
if ! sorted_extensions=$(_sorted_extension_ids "$ext_dir"); then
|
||||
return 2
|
||||
fi
|
||||
while IFS= read -r extension_id; do
|
||||
[ -n "$extension_id" ] || continue
|
||||
local ext="$ext_dir/$extension_id"
|
||||
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" ] || candidate="$ext/${template_name}.md"
|
||||
[ -f "$candidate" ] && echo "$candidate" && return 0
|
||||
done <<< "$sorted_extensions"
|
||||
done
|
||||
fi
|
||||
|
||||
# Priority 4: Core templates
|
||||
@@ -612,8 +492,6 @@ resolve_template_content() {
|
||||
local repo_root="$2"
|
||||
local base="$repo_root/.specify/templates"
|
||||
|
||||
case "$template_name" in ""|*[!a-z0-9-]*) return 1 ;; esac
|
||||
|
||||
# Collect all layers (highest priority first)
|
||||
local -a layer_paths=()
|
||||
local -a layer_strategies=()
|
||||
@@ -621,206 +499,133 @@ resolve_template_content() {
|
||||
# Priority 1: Project overrides (always "replace")
|
||||
local override="$base/overrides/${template_name}.md"
|
||||
if [ -f "$override" ]; then
|
||||
if ! cat "$override"; then
|
||||
echo "Error: failed to read template layer $override" >&2
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
layer_paths+=("$override")
|
||||
layer_strategies+=("replace")
|
||||
fi
|
||||
|
||||
local effective_base_found=false
|
||||
|
||||
# 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"
|
||||
local sorted_presets=""
|
||||
local registry_parsed=false
|
||||
local python_spec=""
|
||||
local -a python_cmd=()
|
||||
if python_spec=$(_python3_command); then
|
||||
read -r -a python_cmd <<< "$python_spec"
|
||||
fi
|
||||
if [ -f "$registry_file" ] && [ "${#python_cmd[@]}" -gt 0 ]; then
|
||||
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" "${python_cmd[@]}" -c "
|
||||
import json, re, sys, os
|
||||
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
||||
if sorted_presets=$(SPECKIT_REGISTRY="$registry_file" python3 -c "
|
||||
import json, sys, os
|
||||
try:
|
||||
with open(os.environ['SPECKIT_REGISTRY'], encoding='utf-8') as f:
|
||||
with open(os.environ['SPECKIT_REGISTRY']) as f:
|
||||
data = json.load(f)
|
||||
presets = data.get('presets', {})
|
||||
def priority(meta):
|
||||
if not isinstance(meta, dict) or isinstance(meta.get('priority'), bool):
|
||||
return 10
|
||||
try:
|
||||
value = int(meta.get('priority', 10))
|
||||
return value if value >= 1 else 10
|
||||
except (TypeError, ValueError, OverflowError):
|
||||
return 10
|
||||
for pid, meta in sorted(presets.items(), key=lambda x: (priority(x[1]), x[0])):
|
||||
if isinstance(meta, dict) and bool(meta.get('enabled', True)) and re.fullmatch(r'[a-z0-9-]+', pid):
|
||||
for pid, meta in sorted(presets.items(), key=lambda x: x[1].get('priority', 10) if isinstance(x[1], dict) else 10):
|
||||
if isinstance(meta, dict) and meta.get('enabled', True) is not False:
|
||||
print(pid)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
" 2>/dev/null); then
|
||||
registry_parsed=true
|
||||
fi
|
||||
fi
|
||||
if [ "$registry_parsed" = false ]; then
|
||||
for preset in "$presets_dir"/*/; do
|
||||
[ -d "$preset" ] || continue
|
||||
local fallback_id
|
||||
fallback_id=$(basename "$preset")
|
||||
case "$fallback_id" in *[!a-z0-9-]*) continue ;; esac
|
||||
sorted_presets+="${sorted_presets:+$'\n'}$fallback_id"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -n "$sorted_presets" ]; then
|
||||
while IFS= read -r preset_id; do
|
||||
local strategy="replace"
|
||||
local manifest_file=""
|
||||
local manifest="$presets_dir/$preset_id/preset.yml"
|
||||
local manifest_declared=false
|
||||
if [ -f "$manifest" ]; then
|
||||
if [ "${#python_cmd[@]}" -eq 0 ]; then
|
||||
echo "Error: Python 3 and PyYAML are required to resolve preset template composition" >&2
|
||||
return 2
|
||||
fi
|
||||
local result
|
||||
local py_stderr
|
||||
local parse_status
|
||||
py_stderr=$(mktemp)
|
||||
if result=$(SPECKIT_MANIFEST="$manifest" SPECKIT_TMPL="$template_name" "${python_cmd[@]}" -c "
|
||||
if [ -n "$sorted_presets" ]; then
|
||||
local yaml_warned=false
|
||||
while IFS= read -r preset_id; do
|
||||
# Read strategy and file path from preset manifest
|
||||
local strategy="replace"
|
||||
local manifest_file=""
|
||||
local manifest="$presets_dir/$preset_id/preset.yml"
|
||||
if [ -f "$manifest" ] && command -v python3 >/dev/null 2>&1; then
|
||||
# Requires PyYAML; falls back to replace/convention if unavailable
|
||||
local result
|
||||
local py_stderr
|
||||
py_stderr=$(mktemp)
|
||||
result=$(SPECKIT_MANIFEST="$manifest" SPECKIT_TMPL="$template_name" python3 -c "
|
||||
import sys, os
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
print('yaml_missing', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
print('replace\t')
|
||||
sys.exit(0)
|
||||
try:
|
||||
with open(os.environ['SPECKIT_MANIFEST'], encoding='utf-8') as f:
|
||||
with open(os.environ['SPECKIT_MANIFEST']) as f:
|
||||
data = yaml.safe_load(f)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError('manifest root must be a mapping')
|
||||
if 'provides' not in data:
|
||||
raise ValueError('manifest missing provides section')
|
||||
provides = data['provides']
|
||||
if not isinstance(provides, dict):
|
||||
raise ValueError('manifest provides must be a mapping')
|
||||
if 'templates' not in provides:
|
||||
raise ValueError('manifest provides missing templates')
|
||||
templates = provides['templates']
|
||||
if not isinstance(templates, list):
|
||||
raise ValueError('manifest templates must be a list')
|
||||
if not templates:
|
||||
raise ValueError('manifest must provide at least one template')
|
||||
valid_types = ('template', 'command', 'script')
|
||||
valid_strategies = ('replace', 'prepend', 'append', 'wrap')
|
||||
for t in templates:
|
||||
if not isinstance(t, dict):
|
||||
raise ValueError('manifest template entries must be mappings')
|
||||
if 'type' not in t or 'name' not in t or 'file' not in t:
|
||||
raise ValueError('manifest template entry missing type, name, or file')
|
||||
for field in ('type', 'name', 'file'):
|
||||
if not isinstance(t[field], str):
|
||||
raise ValueError('manifest template ' + field + ' must be a string')
|
||||
if t['type'] not in valid_types:
|
||||
raise ValueError('invalid manifest template type')
|
||||
strategy = t.get('strategy', 'replace')
|
||||
if not isinstance(strategy, str):
|
||||
raise ValueError('manifest template strategy must be a string')
|
||||
strategy = strategy.lower()
|
||||
if strategy not in valid_strategies:
|
||||
raise ValueError('invalid manifest template strategy')
|
||||
if t['type'] == 'script' and strategy not in ('replace', 'wrap'):
|
||||
raise ValueError('invalid manifest script strategy')
|
||||
for t in templates:
|
||||
for t in data.get('provides', {}).get('templates', []):
|
||||
if t.get('name') == os.environ['SPECKIT_TMPL'] and t.get('type', 'template') == 'template':
|
||||
file_value = t.get('file', '')
|
||||
strategy = t.get('strategy', 'replace')
|
||||
print('found\t' + strategy + '\t' + file_value)
|
||||
print(t.get('strategy', 'replace') + '\t' + t.get('file', ''))
|
||||
sys.exit(0)
|
||||
print('absent\treplace\t')
|
||||
except Exception as exc:
|
||||
print(f'manifest_invalid: {exc}', file=sys.stderr)
|
||||
sys.exit(3)
|
||||
" 2>"$py_stderr"); then
|
||||
parse_status=0
|
||||
else
|
||||
parse_status=$?
|
||||
fi
|
||||
if [ "$parse_status" -ne 0 ]; then
|
||||
if [ "$parse_status" -eq 2 ]; then
|
||||
echo "Error: PyYAML is required to resolve preset template composition" >&2
|
||||
else
|
||||
echo "Error: invalid preset manifest $manifest" >&2
|
||||
print('replace\t')
|
||||
except Exception:
|
||||
print('replace\t')
|
||||
" 2>"$py_stderr")
|
||||
local parse_status=$?
|
||||
if [ $parse_status -eq 0 ] && [ -n "$result" ]; then
|
||||
IFS=$'\t' read -r strategy manifest_file <<< "$result"
|
||||
strategy=$(printf '%s' "$strategy" | tr '[:upper:]' '[:lower:]')
|
||||
fi
|
||||
if [ "$yaml_warned" = false ] && grep -q 'yaml_missing' "$py_stderr" 2>/dev/null; then
|
||||
echo "Warning: PyYAML not available; composition strategies may be ignored" >&2
|
||||
yaml_warned=true
|
||||
fi
|
||||
rm -f "$py_stderr"
|
||||
fi
|
||||
rm -f "$py_stderr"
|
||||
return 2
|
||||
# Try manifest file path first, then convention path
|
||||
local candidate=""
|
||||
if [ -n "$manifest_file" ]; then
|
||||
# Reject absolute paths and parent traversal
|
||||
case "$manifest_file" in
|
||||
/*|*../*|../*) manifest_file="" ;;
|
||||
esac
|
||||
fi
|
||||
if [ -n "$manifest_file" ]; then
|
||||
local mf="$presets_dir/$preset_id/$manifest_file"
|
||||
[ -f "$mf" ] && candidate="$mf"
|
||||
fi
|
||||
if [ -z "$candidate" ]; then
|
||||
local cf="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||
[ -f "$cf" ] && candidate="$cf"
|
||||
fi
|
||||
if [ -n "$candidate" ]; then
|
||||
layer_paths+=("$candidate")
|
||||
layer_strategies+=("$strategy")
|
||||
fi
|
||||
done <<< "$sorted_presets"
|
||||
fi
|
||||
else
|
||||
# python3 failed — fall back to unordered directory scan (replace only)
|
||||
for preset in "$presets_dir"/*/; do
|
||||
[ -d "$preset" ] || continue
|
||||
local candidate="$preset/templates/${template_name}.md"
|
||||
if [ -f "$candidate" ]; then
|
||||
layer_paths+=("$candidate")
|
||||
layer_strategies+=("replace")
|
||||
fi
|
||||
if [ -n "$result" ]; then
|
||||
local declaration
|
||||
IFS=$'\t' read -r declaration strategy manifest_file <<< "$result"
|
||||
[ "$declaration" = "found" ] && manifest_declared=true
|
||||
strategy=$(printf '%s' "$strategy" | tr '[:upper:]' '[:lower:]')
|
||||
fi
|
||||
rm -f "$py_stderr"
|
||||
fi
|
||||
|
||||
local candidate=""
|
||||
if [ -n "$manifest_file" ]; then
|
||||
case "$manifest_file" in
|
||||
/*|*../*|../*) manifest_file="" ;;
|
||||
esac
|
||||
fi
|
||||
if [ -n "$manifest_file" ]; then
|
||||
local mf="$presets_dir/$preset_id/$manifest_file"
|
||||
[ -f "$mf" ] && candidate="$mf"
|
||||
fi
|
||||
if [ -z "$candidate" ] && [ "$manifest_declared" = false ]; then
|
||||
local cf="$presets_dir/$preset_id/templates/${template_name}.md"
|
||||
[ -f "$cf" ] && candidate="$cf"
|
||||
if [ -z "$candidate" ]; then
|
||||
cf="$presets_dir/$preset_id/${template_name}.md"
|
||||
[ -f "$cf" ] && candidate="$cf"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$candidate" ]; then
|
||||
done
|
||||
fi
|
||||
else
|
||||
# No python3 or registry — fall back to unordered directory scan (replace only)
|
||||
for preset in "$presets_dir"/*/; do
|
||||
[ -d "$preset" ] || continue
|
||||
local candidate="$preset/templates/${template_name}.md"
|
||||
if [ -f "$candidate" ]; then
|
||||
layer_paths+=("$candidate")
|
||||
layer_strategies+=("$strategy")
|
||||
if [ "$strategy" = "replace" ]; then
|
||||
effective_base_found=true
|
||||
break
|
||||
fi
|
||||
layer_strategies+=("replace")
|
||||
fi
|
||||
done <<< "$sorted_presets"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Priority 3: Extension-provided templates (always "replace")
|
||||
local ext_dir="$repo_root/.specify/extensions"
|
||||
if [ "$effective_base_found" = false ] && [ -d "$ext_dir" ]; then
|
||||
local sorted_extensions=""
|
||||
if ! sorted_extensions=$(_sorted_extension_ids "$ext_dir"); then
|
||||
return 2
|
||||
fi
|
||||
while IFS= read -r extension_id; do
|
||||
[ -n "$extension_id" ] || continue
|
||||
local ext="$ext_dir/$extension_id"
|
||||
if [ -d "$ext_dir" ]; then
|
||||
for ext in "$ext_dir"/*/; do
|
||||
[ -d "$ext" ] || continue
|
||||
case "$(basename "$ext")" in .*) continue;; esac
|
||||
local candidate="$ext/templates/${template_name}.md"
|
||||
[ -f "$candidate" ] || candidate="$ext/${template_name}.md"
|
||||
if [ -f "$candidate" ]; then
|
||||
layer_paths+=("$candidate")
|
||||
layer_strategies+=("replace")
|
||||
effective_base_found=true
|
||||
break
|
||||
fi
|
||||
done <<< "$sorted_extensions"
|
||||
done
|
||||
fi
|
||||
|
||||
# Priority 4: Core templates (always "replace")
|
||||
local core="$base/${template_name}.md"
|
||||
if [ "$effective_base_found" = false ] && [ -f "$core" ]; then
|
||||
if [ -f "$core" ]; then
|
||||
layer_paths+=("$core")
|
||||
layer_strategies+=("replace")
|
||||
fi
|
||||
@@ -837,18 +642,12 @@ except Exception as exc:
|
||||
# If the top (highest-priority) layer is replace, it wins entirely —
|
||||
# lower layers are irrelevant regardless of their strategies.
|
||||
if [ "${layer_strategies[0]}" = "replace" ]; then
|
||||
if ! cat "${layer_paths[0]}"; then
|
||||
echo "Error: failed to read template layer ${layer_paths[0]}" >&2
|
||||
return 2
|
||||
fi
|
||||
cat "${layer_paths[0]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$has_composition" = false ]; then
|
||||
if ! cat "${layer_paths[0]}"; then
|
||||
echo "Error: failed to read template layer ${layer_paths[0]}" >&2
|
||||
return 2
|
||||
fi
|
||||
cat "${layer_paths[0]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -864,16 +663,12 @@ except Exception as exc:
|
||||
done
|
||||
|
||||
if [ $base_idx -lt 0 ]; then
|
||||
echo "Error: template '$template_name' has composing layers but no replace base" >&2
|
||||
return 2
|
||||
return 1 # no base layer found
|
||||
fi
|
||||
|
||||
# Read the base content; compose layers above the base (higher priority)
|
||||
local content
|
||||
if ! content=$(cat "${layer_paths[$base_idx]}"; status=$?; printf x; exit "$status"); then
|
||||
echo "Error: failed to read template layer ${layer_paths[$base_idx]}" >&2
|
||||
return 2
|
||||
fi
|
||||
content=$(cat "${layer_paths[$base_idx]}"; printf x)
|
||||
content="${content%x}"
|
||||
|
||||
for (( i=base_idx-1; i>=0; i-- )); do
|
||||
@@ -881,26 +676,17 @@ except Exception as exc:
|
||||
local strat="${layer_strategies[$i]}"
|
||||
local layer_content
|
||||
# Preserve trailing newlines
|
||||
if ! layer_content=$(cat "$path"; status=$?; printf x; exit "$status"); then
|
||||
echo "Error: failed to read template layer $path" >&2
|
||||
return 2
|
||||
fi
|
||||
layer_content=$(cat "$path"; printf x)
|
||||
layer_content="${layer_content%x}"
|
||||
|
||||
case "$strat" in
|
||||
replace) content="$layer_content" ;;
|
||||
prepend)
|
||||
content=$(printf '%s\n\n%s' "$layer_content" "$content"; printf x)
|
||||
content="${content%x}"
|
||||
;;
|
||||
append)
|
||||
content=$(printf '%s\n\n%s' "$content" "$layer_content"; printf x)
|
||||
content="${content%x}"
|
||||
;;
|
||||
prepend) content="$(printf '%s\n\n%s' "$layer_content" "$content")" ;;
|
||||
append) content="$(printf '%s\n\n%s' "$content" "$layer_content")" ;;
|
||||
wrap)
|
||||
case "$layer_content" in
|
||||
*'{CORE_TEMPLATE}'*) ;;
|
||||
*) echo "Error: wrap strategy missing {CORE_TEMPLATE} placeholder" >&2; return 2 ;;
|
||||
*) echo "Error: wrap strategy missing {CORE_TEMPLATE} placeholder" >&2; return 1 ;;
|
||||
esac
|
||||
while [[ "$layer_content" == *'{CORE_TEMPLATE}'* ]]; do
|
||||
local before="${layer_content%%\{CORE_TEMPLATE\}*}"
|
||||
@@ -909,7 +695,7 @@ except Exception as exc:
|
||||
done
|
||||
content="$layer_content"
|
||||
;;
|
||||
*) echo "Error: unknown strategy '$strat'" >&2; return 2 ;;
|
||||
*) echo "Error: unknown strategy '$strat'" >&2; return 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ ALLOW_EXISTING=false
|
||||
SHORT_NAME=""
|
||||
BRANCH_NUMBER=""
|
||||
USE_TIMESTAMP=false
|
||||
NUMBER_EXPLICIT=false
|
||||
ARGS=()
|
||||
i=1
|
||||
while [ $i -le $# ]; do
|
||||
@@ -49,9 +48,6 @@ while [ $i -le $# ]; do
|
||||
exit 1
|
||||
fi
|
||||
BRANCH_NUMBER="$next_arg"
|
||||
if [ -n "$BRANCH_NUMBER" ]; then
|
||||
NUMBER_EXPLICIT=true
|
||||
fi
|
||||
;;
|
||||
--timestamp)
|
||||
USE_TIMESTAMP=true
|
||||
@@ -64,7 +60,7 @@ while [ $i -le $# ]; do
|
||||
echo " --dry-run Compute feature name and paths without creating directories or files"
|
||||
echo " --allow-existing-branch Reuse an existing feature directory if it already exists"
|
||||
echo " --short-name <name> Provide a custom short name (2-4 words) for the feature"
|
||||
echo " --number N Prefer a feature number (auto-corrected if its specs prefix exists)"
|
||||
echo " --number N Specify branch number manually (overrides auto-detection)"
|
||||
echo " --timestamp Use timestamp prefix (YYYYMMDD-HHMMSS) instead of sequential numbering"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
@@ -95,7 +91,6 @@ if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||
fi
|
||||
|
||||
MAX_FEATURE_NUMBER=9223372036854775807
|
||||
MAX_BRANCH_LENGTH=244
|
||||
|
||||
is_feature_number_in_range() {
|
||||
local value="$1"
|
||||
@@ -133,40 +128,12 @@ get_highest_from_specs() {
|
||||
echo "$highest"
|
||||
}
|
||||
|
||||
# Return success when a spec directory owns the given numeric prefix.
|
||||
spec_prefix_exists() {
|
||||
local specs_dir="$1"
|
||||
local feature_num="$2"
|
||||
|
||||
for spec_path in "$specs_dir/${feature_num}-"*; do
|
||||
[ -d "$spec_path" ] && return 0
|
||||
done
|
||||
return 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/-$//'
|
||||
}
|
||||
|
||||
# Fit a feature prefix and suffix within GitHub's branch-name limit.
|
||||
fit_branch_name() {
|
||||
local feature_num="$1"
|
||||
local branch_suffix="$2"
|
||||
local branch_name="${feature_num}-${branch_suffix}"
|
||||
|
||||
if [ ${#branch_name} -gt $MAX_BRANCH_LENGTH ]; then
|
||||
local prefix_length=$(( ${#feature_num} + 1 ))
|
||||
local max_suffix_length=$((MAX_BRANCH_LENGTH - prefix_length))
|
||||
local truncated_suffix
|
||||
truncated_suffix=$(printf '%s' "$branch_suffix" | cut -c "1-$max_suffix_length" | sed 's/-$//')
|
||||
branch_name="${feature_num}-${truncated_suffix}"
|
||||
fi
|
||||
|
||||
printf '%s' "$branch_name"
|
||||
}
|
||||
|
||||
# Quote a value for POSIX shell reuse, byte-identical to Python's shlex.quote
|
||||
# so the persistence hints match the Python variant exactly (printf %q output
|
||||
# differs between bash versions and from shlex.quote for spaces/metachars).
|
||||
@@ -286,41 +253,26 @@ else
|
||||
|
||||
# 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))")
|
||||
|
||||
# Treat an explicit number as a preference when its prefix is already used
|
||||
# by a feature directory. Auto-detected numbers are already conflict-free.
|
||||
if [ "$NUMBER_EXPLICIT" = true ]; then
|
||||
SPEC_CONFLICT=false
|
||||
REQUESTED_BRANCH_NAME=$(fit_branch_name "$FEATURE_NUM" "$BRANCH_SUFFIX")
|
||||
REQUESTED_DIR="$SPECS_DIR/$REQUESTED_BRANCH_NAME"
|
||||
if [ "$ALLOW_EXISTING" != true ] || [ ! -d "$REQUESTED_DIR" ]; then
|
||||
spec_prefix_exists "$SPECS_DIR" "$FEATURE_NUM" && SPEC_CONFLICT=true
|
||||
fi
|
||||
|
||||
if [ "$SPEC_CONFLICT" = true ]; then
|
||||
REQUESTED_NUM="$FEATURE_NUM"
|
||||
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
|
||||
BRANCH_NUMBER=$HIGHEST
|
||||
while true; do
|
||||
if [ "$BRANCH_NUMBER" -eq "$MAX_FEATURE_NUMBER" ]; then
|
||||
echo "Error: feature number must be between 0 and $MAX_FEATURE_NUMBER, got '9223372036854775808'" >&2
|
||||
exit 1
|
||||
fi
|
||||
BRANCH_NUMBER=$((BRANCH_NUMBER + 1))
|
||||
FEATURE_NUM=$(printf "%03d" "$((10#$BRANCH_NUMBER))")
|
||||
spec_prefix_exists "$SPECS_DIR" "$FEATURE_NUM" || break
|
||||
done
|
||||
>&2 echo "[specify] Warning: --number $REQUESTED_NUM conflicts with an existing spec directory; using $FEATURE_NUM instead"
|
||||
fi
|
||||
fi
|
||||
|
||||
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
||||
fi
|
||||
|
||||
# GitHub enforces a 244-byte limit on branch names
|
||||
# Validate and truncate if necessary
|
||||
ORIGINAL_BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
||||
BRANCH_NAME=$(fit_branch_name "$FEATURE_NUM" "$BRANCH_SUFFIX")
|
||||
if [ "$BRANCH_NAME" != "$ORIGINAL_BRANCH_NAME" ]; then
|
||||
MAX_BRANCH_LENGTH=244
|
||||
if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
|
||||
# Calculate how much we need to trim from suffix
|
||||
# Account for prefix length: timestamp (15) + hyphen (1) = 16, or sequential (3) + hyphen (1) = 4
|
||||
PREFIX_LENGTH=$(( ${#FEATURE_NUM} + 1 ))
|
||||
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - PREFIX_LENGTH))
|
||||
|
||||
# Truncate suffix at word boundary if possible
|
||||
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
|
||||
# Remove trailing hyphen if truncation created one
|
||||
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
|
||||
|
||||
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
|
||||
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
|
||||
|
||||
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
|
||||
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
|
||||
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
|
||||
@@ -339,27 +291,12 @@ if [ "$DRY_RUN" != true ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NEEDS_SPEC=false
|
||||
SPEC_TEMPLATE_FOUND=false
|
||||
SPEC_TEMPLATE_CONTENT=""
|
||||
if [ ! -f "$SPEC_FILE" ]; then
|
||||
NEEDS_SPEC=true
|
||||
if SPEC_TEMPLATE_CONTENT=$(resolve_template_content "spec-template" "$REPO_ROOT"; status=$?; printf x; exit "$status"); then
|
||||
SPEC_TEMPLATE_CONTENT="${SPEC_TEMPLATE_CONTENT%x}"
|
||||
SPEC_TEMPLATE_FOUND=true
|
||||
else
|
||||
resolve_status=$?
|
||||
if [ "$resolve_status" -ne 1 ]; then
|
||||
exit "$resolve_status"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$FEATURE_DIR"
|
||||
|
||||
if [ "$NEEDS_SPEC" = true ]; then
|
||||
if [ "$SPEC_TEMPLATE_FOUND" = true ]; then
|
||||
printf '%s' "$SPEC_TEMPLATE_CONTENT" > "$SPEC_FILE"
|
||||
if [ ! -f "$SPEC_FILE" ]; then
|
||||
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT") || true
|
||||
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then
|
||||
cp "$TEMPLATE" "$SPEC_FILE"
|
||||
else
|
||||
echo "Warning: Spec template not found; created empty spec file" >&2
|
||||
touch "$SPEC_FILE"
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(CDPATH="" cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
JSON_MODE=false
|
||||
TEMPLATE_NAME=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--json) JSON_MODE=true ;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 <template-name> [--json]"
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "ERROR: Unknown option '$arg'" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -n "$TEMPLATE_NAME" ]]; then
|
||||
echo "ERROR: Unexpected argument '$arg'" >&2
|
||||
exit 1
|
||||
fi
|
||||
TEMPLATE_NAME="$arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$TEMPLATE_NAME" ]]; then
|
||||
echo "ERROR: Template name is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(get_repo_root)
|
||||
if TEMPLATE_CONTENT=$(resolve_template_content "$TEMPLATE_NAME" "$REPO_ROOT"; status=$?; printf x; exit "$status"); then
|
||||
TEMPLATE_CONTENT="${TEMPLATE_CONTENT%x}"
|
||||
else
|
||||
echo "ERROR: Could not resolve required $TEMPLATE_NAME from the template override stack for $REPO_ROOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if $JSON_MODE; then
|
||||
if has_jq; then
|
||||
jq -cn \
|
||||
--arg template_name "$TEMPLATE_NAME" \
|
||||
--arg template_content "$TEMPLATE_CONTENT" \
|
||||
'{TEMPLATE_NAME:$template_name,TEMPLATE_CONTENT:$template_content}'
|
||||
else
|
||||
printf '{"TEMPLATE_NAME":"%s","TEMPLATE_CONTENT":"%s"}\n' \
|
||||
"$(json_escape "$TEMPLATE_NAME")" "$(json_escape "$TEMPLATE_CONTENT")"
|
||||
fi
|
||||
else
|
||||
printf '%s' "$TEMPLATE_CONTENT"
|
||||
fi
|
||||
@@ -43,23 +43,21 @@ if [[ -f "$IMPL_PLAN" ]]; then
|
||||
echo "Plan already exists at $IMPL_PLAN, skipping template copy"
|
||||
fi
|
||||
else
|
||||
if resolve_template_content "plan-template" "$REPO_ROOT" > "$IMPL_PLAN"; then
|
||||
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT") || true
|
||||
if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then
|
||||
cp "$TEMPLATE" "$IMPL_PLAN"
|
||||
if $JSON_MODE; then
|
||||
echo "Copied plan template to $IMPL_PLAN" >&2
|
||||
else
|
||||
echo "Copied plan template to $IMPL_PLAN"
|
||||
fi
|
||||
else
|
||||
resolve_status=$?
|
||||
rm -f "$IMPL_PLAN"
|
||||
if [ "$resolve_status" -ne 1 ]; then
|
||||
exit "$resolve_status"
|
||||
fi
|
||||
if $JSON_MODE; then
|
||||
echo "Warning: Plan template not found" >&2
|
||||
else
|
||||
echo "Warning: Plan template not found"
|
||||
fi
|
||||
# Create a basic plan file if template doesn't exist
|
||||
touch "$IMPL_PLAN"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -51,9 +51,7 @@ fi
|
||||
|
||||
# Resolve tasks template through override stack
|
||||
TASKS_TEMPLATE=$(resolve_template "tasks-template" "$REPO_ROOT") || true
|
||||
if TASKS_TEMPLATE_CONTENT=$(resolve_template_content "tasks-template" "$REPO_ROOT"; status=$?; printf x; exit "$status"); then
|
||||
TASKS_TEMPLATE_CONTENT="${TASKS_TEMPLATE_CONTENT%x}"
|
||||
else
|
||||
if [[ -z "$TASKS_TEMPLATE" ]] || [[ ! -f "$TASKS_TEMPLATE" ]]; then
|
||||
echo "ERROR: Could not resolve required tasks-template from the template override stack for $REPO_ROOT" >&2
|
||||
echo "Template 'tasks-template' was not found in any supported location (overrides, presets, extensions, or shared core). Add an override at .specify/templates/overrides/tasks-template.md, or run 'specify init' / reinstall shared infra to restore the core .specify/templates/tasks-template.md template." >&2
|
||||
exit 1
|
||||
@@ -71,8 +69,7 @@ if $JSON_MODE; then
|
||||
--arg feature_dir "$FEATURE_DIR" \
|
||||
--argjson docs "$json_docs" \
|
||||
--arg tasks_template "${TASKS_TEMPLATE:-}" \
|
||||
--arg tasks_template_content "$TASKS_TEMPLATE_CONTENT" \
|
||||
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs,TASKS_TEMPLATE:$tasks_template,TASKS_TEMPLATE_CONTENT:$tasks_template_content}'
|
||||
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs,TASKS_TEMPLATE:$tasks_template}'
|
||||
else
|
||||
if [[ ${#docs[@]} -eq 0 ]]; then
|
||||
json_docs="[]"
|
||||
@@ -80,8 +77,8 @@ if $JSON_MODE; then
|
||||
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done)
|
||||
json_docs="[${json_docs%,}]"
|
||||
fi
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"TASKS_TEMPLATE":"%s","TASKS_TEMPLATE_CONTENT":"%s"}\n' \
|
||||
"$(json_escape "$FEATURE_DIR")" "$json_docs" "$(json_escape "${TASKS_TEMPLATE:-}")" "$(json_escape "$TASKS_TEMPLATE_CONTENT")"
|
||||
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"TASKS_TEMPLATE":"%s"}\n' \
|
||||
"$(json_escape "$FEATURE_DIR")" "$json_docs" "$(json_escape "${TASKS_TEMPLATE:-}")"
|
||||
fi
|
||||
else
|
||||
echo "FEATURE_DIR: $FEATURE_DIR"
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
# -RequireTasks Require tasks.md to exist (for implementation phase)
|
||||
# -IncludeTasks Include tasks.md in AVAILABLE_DOCS list
|
||||
# -PathsOnly Only output path variables (no validation)
|
||||
# -Template NAME Include composed template content in JSON output
|
||||
# -Help, -h Show help message
|
||||
|
||||
[CmdletBinding()]
|
||||
@@ -21,7 +20,6 @@ param(
|
||||
[switch]$RequireTasks,
|
||||
[switch]$IncludeTasks,
|
||||
[switch]$PathsOnly,
|
||||
[string]$Template,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
@@ -39,7 +37,6 @@ OPTIONS:
|
||||
-RequireTasks Require tasks.md to exist (for implementation phase)
|
||||
-IncludeTasks Include tasks.md in AVAILABLE_DOCS list
|
||||
-PathsOnly Only output path variables (no prerequisite validation)
|
||||
-Template NAME Include composed template content in JSON output
|
||||
-Help, -h Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
@@ -132,43 +129,25 @@ if ($IncludeTasks -and (Test-Path $paths.TASKS)) {
|
||||
$docs += 'tasks.md'
|
||||
}
|
||||
|
||||
$templateContent = $null
|
||||
if ($Template) {
|
||||
$templateContent = Resolve-TemplateContent -TemplateName $Template -RepoRoot $paths.REPO_ROOT
|
||||
if ($null -eq $templateContent) {
|
||||
[Console]::Error.WriteLine("ERROR: Could not resolve required $Template from the template override stack for $($paths.REPO_ROOT)")
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Output results
|
||||
if ($Json) {
|
||||
# JSON output
|
||||
$result = [ordered]@{
|
||||
[PSCustomObject]@{
|
||||
FEATURE_DIR = $paths.FEATURE_DIR
|
||||
AVAILABLE_DOCS = $docs
|
||||
}
|
||||
if ($Template) {
|
||||
$result.TEMPLATE_CONTENT = $templateContent
|
||||
}
|
||||
[PSCustomObject]$result | ConvertTo-Json -Compress
|
||||
} | ConvertTo-Json -Compress
|
||||
} else {
|
||||
# Text output
|
||||
Write-Output "FEATURE_DIR:$($paths.FEATURE_DIR)"
|
||||
Write-Output "AVAILABLE_DOCS:"
|
||||
|
||||
# Show status of each potential document.
|
||||
# These helpers report their line with Write-Output and ALSO return a
|
||||
# bool, both on the Success stream, so 'Out-Null' discarded the report
|
||||
# line along with the return value and left AVAILABLE_DOCS empty. Drop
|
||||
# only the boolean so the per-document lines reach stdout like the
|
||||
# bash and Python twins.
|
||||
Test-FileExists -Path $paths.RESEARCH -Description 'research.md' | Where-Object { $_ -isnot [bool] }
|
||||
Test-FileExists -Path $paths.DATA_MODEL -Description 'data-model.md' | Where-Object { $_ -isnot [bool] }
|
||||
Test-DirHasFiles -Path $paths.CONTRACTS_DIR -Description 'contracts/' | Where-Object { $_ -isnot [bool] }
|
||||
Test-FileExists -Path $paths.QUICKSTART -Description 'quickstart.md' | Where-Object { $_ -isnot [bool] }
|
||||
# Show status of each potential document
|
||||
Test-FileExists -Path $paths.RESEARCH -Description 'research.md' | Out-Null
|
||||
Test-FileExists -Path $paths.DATA_MODEL -Description 'data-model.md' | Out-Null
|
||||
Test-DirHasFiles -Path $paths.CONTRACTS_DIR -Description 'contracts/' | Out-Null
|
||||
Test-FileExists -Path $paths.QUICKSTART -Description 'quickstart.md' | Out-Null
|
||||
|
||||
if ($IncludeTasks) {
|
||||
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Where-Object { $_ -isnot [bool] }
|
||||
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
+85
-250
@@ -55,17 +55,9 @@ function Resolve-SpecifyInitDir {
|
||||
}
|
||||
# Resolve-Path echoes back any trailing separator from the input; trim it so
|
||||
# the returned root matches the bash resolver, whose `cd && pwd` never yields
|
||||
# one. TrimEnd (not [Path]::TrimEndingDirectorySeparator, which is .NET Core
|
||||
# only) keeps this working on Windows PowerShell 5.1 / .NET Framework, as
|
||||
# Get-FeaturePathsEnv already does below. Unlike a bare TrimEnd, the
|
||||
# GetPathRoot check preserves a path that *is* its own root ('C:\' must not
|
||||
# become 'C:', which every later API re-resolves against the current
|
||||
# directory instead of the drive root). No-op on a path with no trailing
|
||||
# separator.
|
||||
$initRoot = $resolved.Path.TrimEnd('/', '\')
|
||||
if ($initRoot.Length -lt [System.IO.Path]::GetPathRoot($resolved.Path).Length) {
|
||||
$initRoot = $resolved.Path
|
||||
}
|
||||
# one. TrimEndingDirectorySeparator is a no-op on a bare root and on a path
|
||||
# that already has no trailing separator.
|
||||
$initRoot = [System.IO.Path]::TrimEndingDirectorySeparator($resolved.Path)
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $initRoot '.specify') -PathType Container)) {
|
||||
[Console]::Error.WriteLine("ERROR: SPECIFY_INIT_DIR is not a Spec Kit project (no .specify/ directory): $initRoot")
|
||||
if ($ReturnNullOnError) { return $null }
|
||||
@@ -332,82 +324,6 @@ function Get-Python3Command {
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-NormalizedPriority {
|
||||
param($Value)
|
||||
|
||||
if ($Value -is [bool]) { return 10 }
|
||||
if ($Value -is [string]) {
|
||||
$integerText = $Value.Trim()
|
||||
if ($integerText -cnotmatch '^[+-]?[0-9]+(?:_[0-9]+)*$') { return 10 }
|
||||
$Value = $integerText.Replace('_', '')
|
||||
}
|
||||
try {
|
||||
$parsedPriority = [System.Numerics.BigInteger]$Value
|
||||
} catch {
|
||||
return 10
|
||||
}
|
||||
return $(if ($parsedPriority -ge 1) { $parsedPriority } else { 10 })
|
||||
}
|
||||
|
||||
function Get-SortedExtensionIds {
|
||||
param([Parameter(Mandatory=$true)][string]$ExtensionsDir)
|
||||
|
||||
$registeredNames = @()
|
||||
$ranked = @()
|
||||
$registryFile = Join-Path $ExtensionsDir '.registry'
|
||||
# Detect any filesystem entry at the registry path without following symlinks.
|
||||
# Test-Path follows links and reports $false for a dangling symlink, so a
|
||||
# broken .registry symlink would otherwise bypass this guard and let the
|
||||
# directory scan below enable every on-disk extension. Enumerating the parent
|
||||
# directory still observes a broken symlink as an entry.
|
||||
$registryEntry = Get-ChildItem -LiteralPath $ExtensionsDir -Force -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -eq '.registry' } |
|
||||
Select-Object -First 1
|
||||
if ($registryEntry) {
|
||||
if (-not (Test-Path -LiteralPath $registryFile -PathType Leaf)) {
|
||||
throw "Invalid extension registry ${registryFile}: not a regular file"
|
||||
}
|
||||
try {
|
||||
$data = [System.IO.File]::ReadAllText($registryFile, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
} catch {
|
||||
throw "Invalid extension registry ${registryFile}: $($_.Exception.Message)"
|
||||
}
|
||||
if ($null -eq $data -or $data -isnot [PSCustomObject]) {
|
||||
throw "Invalid extension registry ${registryFile}: root must be a mapping"
|
||||
}
|
||||
$extensionsProperty = $data.PSObject.Properties['extensions']
|
||||
if ($extensionsProperty) {
|
||||
if ($extensionsProperty.Value -isnot [PSCustomObject]) {
|
||||
throw "Invalid extension registry ${registryFile}: 'extensions' must be a mapping"
|
||||
}
|
||||
$extensions = $extensionsProperty.Value
|
||||
} else {
|
||||
$extensions = [PSCustomObject]@{}
|
||||
}
|
||||
$registeredNames = @($extensions.PSObject.Properties | ForEach-Object { $_.Name })
|
||||
foreach ($entry in $extensions.PSObject.Properties) {
|
||||
if ($entry.Name -cnotmatch '^[a-z0-9-]+$' -or $entry.Value -isnot [PSCustomObject]) {
|
||||
continue
|
||||
}
|
||||
$enabledProperty = $entry.Value.PSObject.Properties['enabled']
|
||||
if ($enabledProperty -and -not [bool]$enabledProperty.Value) { continue }
|
||||
$priority = 10
|
||||
$priorityProperty = $entry.Value.PSObject.Properties['priority']
|
||||
if ($priorityProperty) {
|
||||
$priority = Get-NormalizedPriority -Value $priorityProperty.Value
|
||||
}
|
||||
$ranked += [PSCustomObject]@{ Priority = $priority; Id = $entry.Name }
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($directory in Get-ChildItem -Path $ExtensionsDir -Directory -ErrorAction SilentlyContinue) {
|
||||
if ($directory.Name -cmatch '^[a-z0-9-]+$' -and $directory.Name -cnotin $registeredNames) {
|
||||
$ranked += [PSCustomObject]@{ Priority = 10; Id = $directory.Name }
|
||||
}
|
||||
}
|
||||
return $ranked | Sort-Object Priority, Id | ForEach-Object { $_.Id }
|
||||
}
|
||||
|
||||
# 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)
|
||||
@@ -419,8 +335,6 @@ function Resolve-Template {
|
||||
[Parameter(Mandatory=$true)][string]$RepoRoot
|
||||
)
|
||||
|
||||
if ($TemplateName -cnotmatch '^[a-z0-9-]+$') { return $null }
|
||||
|
||||
$base = Join-Path $RepoRoot '.specify/templates'
|
||||
|
||||
# Priority 1: Project overrides
|
||||
@@ -435,7 +349,7 @@ function Resolve-Template {
|
||||
$registryParsed = $false
|
||||
if (Test-Path $registryFile) {
|
||||
try {
|
||||
$registryData = [System.IO.File]::ReadAllText($registryFile, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
$registryData = Get-Content $registryFile -Raw | ConvertFrom-Json
|
||||
if ($null -eq $registryData -or $registryData -isnot [PSCustomObject]) {
|
||||
throw 'Registry root must be an object'
|
||||
}
|
||||
@@ -450,20 +364,30 @@ function Resolve-Template {
|
||||
param($Entry)
|
||||
if ($Entry.Value -is [PSCustomObject]) {
|
||||
$priorityProperty = $Entry.Value.PSObject.Properties['priority']
|
||||
if ($priorityProperty) {
|
||||
return Get-NormalizedPriority -Value $priorityProperty.Value
|
||||
}
|
||||
if ($priorityProperty) { return $priorityProperty.Value }
|
||||
}
|
||||
return 10
|
||||
}
|
||||
if ($presetEntries.Count -gt 1) {
|
||||
$allNumeric = $true
|
||||
$allStrings = $true
|
||||
foreach ($entry in $presetEntries) {
|
||||
$priority = & $priorityFor $entry
|
||||
if ($null -eq $priority -or $priority -isnot [ValueType]) {
|
||||
$allNumeric = $false
|
||||
}
|
||||
if ($null -eq $priority -or $priority -isnot [string]) {
|
||||
$allStrings = $false
|
||||
}
|
||||
}
|
||||
if (-not $allNumeric -and -not $allStrings) {
|
||||
throw 'Registry priorities are not mutually orderable'
|
||||
}
|
||||
}
|
||||
$sortedPresets = $presetEntries |
|
||||
Where-Object { $_.Value -is [PSCustomObject] } |
|
||||
Where-Object {
|
||||
$enabled = $_.Value.PSObject.Properties['enabled']
|
||||
-not $enabled -or [bool]$enabled.Value
|
||||
} |
|
||||
Where-Object { $_.Name -cmatch '^[a-z0-9-]+$' } |
|
||||
Sort-Object @{ Expression = { & $priorityFor $_ } }, @{ Expression = { $_.Name } } |
|
||||
Where-Object { $null -eq $_.Value.enabled -or $_.Value.enabled -ne $false } |
|
||||
Sort-Object { & $priorityFor $_ } |
|
||||
ForEach-Object { $_.Name }
|
||||
}
|
||||
$registryParsed = $true
|
||||
@@ -476,16 +400,12 @@ function Resolve-Template {
|
||||
foreach ($presetId in $sortedPresets) {
|
||||
$candidate = Join-Path $presetsDir "$presetId/templates/$TemplateName.md"
|
||||
if (Test-Path $candidate) { return $candidate }
|
||||
$candidate = Join-Path $presetsDir "$presetId/$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 '.*' } | Sort-Object Name) {
|
||||
$candidate = Join-Path $preset.FullName "templates/$TemplateName.md"
|
||||
if (Test-Path $candidate) { return $candidate }
|
||||
$candidate = Join-Path $preset.FullName "$TemplateName.md"
|
||||
if (Test-Path $candidate) { return $candidate }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,11 +413,8 @@ function Resolve-Template {
|
||||
# Priority 3: Extension-provided templates
|
||||
$extDir = Join-Path $RepoRoot '.specify/extensions'
|
||||
if (Test-Path $extDir) {
|
||||
foreach ($extensionId in Get-SortedExtensionIds -ExtensionsDir $extDir) {
|
||||
$candidate = Join-Path $extDir "$extensionId/templates/$TemplateName.md"
|
||||
if (-not (Test-Path $candidate)) {
|
||||
$candidate = Join-Path $extDir "$extensionId/$TemplateName.md"
|
||||
}
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -518,10 +435,6 @@ function Resolve-TemplateContent {
|
||||
[Parameter(Mandatory=$true)][string]$RepoRoot
|
||||
)
|
||||
|
||||
if ($TemplateName -cnotmatch '^[a-z0-9-]+$') {
|
||||
return $null
|
||||
}
|
||||
|
||||
$base = Join-Path $RepoRoot '.specify/templates'
|
||||
|
||||
# Collect all layers (highest priority first)
|
||||
@@ -531,77 +444,49 @@ function Resolve-TemplateContent {
|
||||
# Priority 1: Project overrides (always "replace")
|
||||
$override = Join-Path $base "overrides/$TemplateName.md"
|
||||
if (Test-Path $override) {
|
||||
return [System.IO.File]::ReadAllText(
|
||||
$override,
|
||||
[System.Text.Encoding]::UTF8
|
||||
)
|
||||
$layerPaths += $override
|
||||
$layerStrategies += 'replace'
|
||||
}
|
||||
|
||||
$effectiveBaseFound = $false
|
||||
|
||||
# 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 = @()
|
||||
$registryParsed = $false
|
||||
if (Test-Path $registryFile) {
|
||||
try {
|
||||
$registryData = [System.IO.File]::ReadAllText($registryFile, [System.Text.Encoding]::UTF8) | ConvertFrom-Json
|
||||
if ($null -eq $registryData -or $registryData -isnot [PSCustomObject]) {
|
||||
throw 'Registry root must be an object'
|
||||
}
|
||||
$presetsProperty = $registryData.PSObject.Properties['presets']
|
||||
if ($presetsProperty) {
|
||||
$presets = $presetsProperty.Value
|
||||
if ($null -eq $presets -or $presets -isnot [PSCustomObject]) {
|
||||
throw 'Registry presets must be an object'
|
||||
}
|
||||
$presetEntries = @($presets.PSObject.Properties)
|
||||
$priorityFor = {
|
||||
param($Entry)
|
||||
if ($Entry.Value -is [PSCustomObject]) {
|
||||
$priorityProperty = $Entry.Value.PSObject.Properties['priority']
|
||||
if ($priorityProperty) {
|
||||
return Get-NormalizedPriority -Value $priorityProperty.Value
|
||||
}
|
||||
}
|
||||
return 10
|
||||
}
|
||||
$sortedPresets = $presetEntries |
|
||||
Where-Object { $_.Value -is [PSCustomObject] } |
|
||||
Where-Object {
|
||||
$enabled = $_.Value.PSObject.Properties['enabled']
|
||||
-not $enabled -or [bool]$enabled.Value
|
||||
} |
|
||||
Where-Object { $_.Name -cmatch '^[a-z0-9-]+$' } |
|
||||
Sort-Object @{ Expression = { & $priorityFor $_ } }, @{ Expression = { $_.Name } } |
|
||||
$registryData = Get-Content $registryFile -Raw | ConvertFrom-Json
|
||||
$presets = $registryData.presets
|
||||
if ($presets) {
|
||||
$sortedPresets = $presets.PSObject.Properties |
|
||||
Where-Object { $null -eq $_.Value.enabled -or $_.Value.enabled -ne $false } |
|
||||
Sort-Object { if ($null -ne $_.Value.priority) { $_.Value.priority } else { 10 } } |
|
||||
ForEach-Object { $_.Name }
|
||||
}
|
||||
$registryParsed = $true
|
||||
} catch {
|
||||
$registryParsed = $false
|
||||
$sortedPresets = @()
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $registryParsed) {
|
||||
$sortedPresets = Get-ChildItem -Path $presetsDir -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -cmatch '^[a-z0-9-]+$' } |
|
||||
Sort-Object Name |
|
||||
ForEach-Object { $_.Name }
|
||||
}
|
||||
|
||||
$pyCmd = @(Get-Python3Command)
|
||||
foreach ($presetId in $sortedPresets) {
|
||||
if ($sortedPresets.Count -gt 0) {
|
||||
$pyCmd = Get-Python3Command
|
||||
if (-not $pyCmd) {
|
||||
# Check if any preset has strategy fields that would be ignored
|
||||
foreach ($pid in $sortedPresets) {
|
||||
$mf = Join-Path $presetsDir "$pid/preset.yml"
|
||||
if ((Test-Path $mf) -and (Select-String -Path $mf -Pattern 'strategy:' -Quiet -ErrorAction SilentlyContinue)) {
|
||||
Write-Warning "No Python 3 found; preset composition strategies will be ignored"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
$yamlWarned = $false
|
||||
foreach ($presetId in $sortedPresets) {
|
||||
# Read strategy and file path from preset manifest
|
||||
$strategy = 'replace'
|
||||
$manifestFilePath = ''
|
||||
$manifestDeclared = $false
|
||||
$manifest = Join-Path $presetsDir "$presetId/preset.yml"
|
||||
if ((Test-Path $manifest) -and -not $pyCmd) {
|
||||
throw "Python 3 and PyYAML are required to resolve preset template composition"
|
||||
}
|
||||
if (Test-Path $manifest) {
|
||||
if ((Test-Path $manifest) -and $pyCmd) {
|
||||
try {
|
||||
# Use Python to parse YAML manifest for strategy and file path
|
||||
$pyArgs = if ($pyCmd.Count -gt 1) { $pyCmd[1..($pyCmd.Count-1)] } else { @() }
|
||||
@@ -612,71 +497,32 @@ try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
print('yaml_missing', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
print('replace\t')
|
||||
sys.exit(0)
|
||||
try:
|
||||
with open(sys.argv[1], encoding='utf-8') as f:
|
||||
with open(sys.argv[1]) as f:
|
||||
data = yaml.safe_load(f)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError('manifest root must be a mapping')
|
||||
if 'provides' not in data:
|
||||
raise ValueError('manifest missing provides section')
|
||||
provides = data['provides']
|
||||
if not isinstance(provides, dict):
|
||||
raise ValueError('manifest provides must be a mapping')
|
||||
if 'templates' not in provides:
|
||||
raise ValueError('manifest provides missing templates')
|
||||
templates = provides['templates']
|
||||
if not isinstance(templates, list):
|
||||
raise ValueError('manifest templates must be a list')
|
||||
if not templates:
|
||||
raise ValueError('manifest must provide at least one template')
|
||||
valid_types = ('template', 'command', 'script')
|
||||
valid_strategies = ('replace', 'prepend', 'append', 'wrap')
|
||||
for t in templates:
|
||||
if not isinstance(t, dict):
|
||||
raise ValueError('manifest template entries must be mappings')
|
||||
if 'type' not in t or 'name' not in t or 'file' not in t:
|
||||
raise ValueError('manifest template entry missing type, name, or file')
|
||||
for field in ('type', 'name', 'file'):
|
||||
if not isinstance(t[field], str):
|
||||
raise ValueError('manifest template ' + field + ' must be a string')
|
||||
if t['type'] not in valid_types:
|
||||
raise ValueError('invalid manifest template type')
|
||||
strategy = t.get('strategy', 'replace')
|
||||
if not isinstance(strategy, str):
|
||||
raise ValueError('manifest template strategy must be a string')
|
||||
strategy = strategy.lower()
|
||||
if strategy not in valid_strategies:
|
||||
raise ValueError('invalid manifest template strategy')
|
||||
if t['type'] == 'script' and strategy not in ('replace', 'wrap'):
|
||||
raise ValueError('invalid manifest script strategy')
|
||||
for t in templates:
|
||||
for t in data.get('provides', {}).get('templates', []):
|
||||
if t.get('name') == sys.argv[2] and t.get('type', 'template') == 'template':
|
||||
file_value = t.get('file', '')
|
||||
strategy = t.get('strategy', 'replace')
|
||||
print('found\t' + strategy + '\t' + file_value)
|
||||
print(t.get('strategy', 'replace') + '\t' + t.get('file', ''))
|
||||
sys.exit(0)
|
||||
print('absent\treplace\t')
|
||||
except Exception as exc:
|
||||
print(f'manifest_invalid: {exc}', file=sys.stderr)
|
||||
sys.exit(3)
|
||||
print('replace\t')
|
||||
except Exception:
|
||||
print('replace\t')
|
||||
"@ $manifest $TemplateName 2>$pyStderrFile
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
if ($LASTEXITCODE -eq 2) {
|
||||
throw "PyYAML is required to resolve preset template composition"
|
||||
}
|
||||
throw "Invalid preset manifest $manifest"
|
||||
}
|
||||
if ($stratResult) {
|
||||
$parts = $stratResult.Trim() -split "`t", 3
|
||||
$manifestDeclared = $parts[0] -eq 'found'
|
||||
$strategy = $parts[1].ToLowerInvariant()
|
||||
if ($parts.Count -gt 2 -and $parts[2]) { $manifestFilePath = $parts[2] }
|
||||
$parts = $stratResult.Trim() -split "`t", 2
|
||||
$strategy = $parts[0].ToLowerInvariant()
|
||||
if ($parts.Count -gt 1 -and $parts[1]) { $manifestFilePath = $parts[1] }
|
||||
}
|
||||
if (-not $yamlWarned -and (Test-Path $pyStderrFile) -and (Get-Content $pyStderrFile -Raw -ErrorAction SilentlyContinue) -match 'yaml_missing') {
|
||||
Write-Warning "PyYAML not available; composition strategies may be ignored"
|
||||
$yamlWarned = $true
|
||||
}
|
||||
Remove-Item $pyStderrFile -Force -ErrorAction SilentlyContinue
|
||||
} catch {
|
||||
$strategy = 'replace'
|
||||
if ($pyStderrFile) { Remove-Item $pyStderrFile -Force -ErrorAction SilentlyContinue }
|
||||
throw
|
||||
}
|
||||
}
|
||||
# Try manifest file path first, then convention path
|
||||
@@ -691,45 +537,42 @@ except Exception as exc:
|
||||
$mf = Join-Path $presetsDir "$presetId/$manifestFilePath"
|
||||
if (Test-Path $mf) { $candidate = $mf }
|
||||
}
|
||||
if (-not $candidate -and -not $manifestDeclared) {
|
||||
if (-not $candidate) {
|
||||
$cf = Join-Path $presetsDir "$presetId/templates/$TemplateName.md"
|
||||
if (Test-Path $cf) { $candidate = $cf }
|
||||
if (-not $candidate) {
|
||||
$cf = Join-Path $presetsDir "$presetId/$TemplateName.md"
|
||||
if (Test-Path $cf) { $candidate = $cf }
|
||||
}
|
||||
}
|
||||
if ($candidate) {
|
||||
$layerPaths += $candidate
|
||||
$layerStrategies += $strategy
|
||||
if ($strategy -eq 'replace') {
|
||||
$effectiveBaseFound = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Fallback: alphabetical directory order (no registry or parse failure)
|
||||
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) {
|
||||
$layerPaths += $candidate
|
||||
$layerStrategies += 'replace'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Priority 3: Extension-provided templates (always "replace")
|
||||
$extDir = Join-Path $RepoRoot '.specify/extensions'
|
||||
if (-not $effectiveBaseFound -and (Test-Path $extDir)) {
|
||||
foreach ($extensionId in Get-SortedExtensionIds -ExtensionsDir $extDir) {
|
||||
$candidate = Join-Path $extDir "$extensionId/templates/$TemplateName.md"
|
||||
if (-not (Test-Path $candidate)) {
|
||||
$candidate = Join-Path $extDir "$extensionId/$TemplateName.md"
|
||||
}
|
||||
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) {
|
||||
$layerPaths += $candidate
|
||||
$layerStrategies += 'replace'
|
||||
$effectiveBaseFound = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Priority 4: Core templates (always "replace")
|
||||
$core = Join-Path $base "$TemplateName.md"
|
||||
if (-not $effectiveBaseFound -and (Test-Path $core)) {
|
||||
if (Test-Path $core) {
|
||||
$layerPaths += $core
|
||||
$layerStrategies += 'replace'
|
||||
}
|
||||
@@ -739,7 +582,7 @@ except Exception as exc:
|
||||
# If the top (highest-priority) layer is replace, it wins entirely --
|
||||
# lower layers are irrelevant regardless of their strategies.
|
||||
if ($layerStrategies[0] -eq 'replace') {
|
||||
return [System.IO.File]::ReadAllText($layerPaths[0], [System.Text.Encoding]::UTF8)
|
||||
return (Get-Content $layerPaths[0] -Raw)
|
||||
}
|
||||
|
||||
# Check if any layer uses a non-replace strategy
|
||||
@@ -749,7 +592,7 @@ except Exception as exc:
|
||||
}
|
||||
|
||||
if (-not $hasComposition) {
|
||||
return [System.IO.File]::ReadAllText($layerPaths[0], [System.Text.Encoding]::UTF8)
|
||||
return (Get-Content $layerPaths[0] -Raw)
|
||||
}
|
||||
|
||||
# Find the effective base: scan from highest priority (index 0) downward
|
||||
@@ -761,22 +604,14 @@ except Exception as exc:
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($baseIdx -lt 0) {
|
||||
throw "Template '$TemplateName' has composing layers but no replace base"
|
||||
}
|
||||
if ($baseIdx -lt 0) { return $null }
|
||||
|
||||
$content = [System.IO.File]::ReadAllText(
|
||||
$layerPaths[$baseIdx],
|
||||
[System.Text.Encoding]::UTF8
|
||||
)
|
||||
$content = Get-Content $layerPaths[$baseIdx] -Raw
|
||||
|
||||
for ($i = $baseIdx - 1; $i -ge 0; $i--) {
|
||||
$path = $layerPaths[$i]
|
||||
$strat = $layerStrategies[$i]
|
||||
$layerContent = [System.IO.File]::ReadAllText(
|
||||
$path,
|
||||
[System.Text.Encoding]::UTF8
|
||||
)
|
||||
$layerContent = Get-Content $path -Raw
|
||||
|
||||
switch ($strat) {
|
||||
'replace' { $content = $layerContent }
|
||||
|
||||
@@ -14,7 +14,6 @@ param(
|
||||
[string[]]$FeatureDescription
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$maxBranchLength = 244
|
||||
|
||||
# Show help if requested
|
||||
if ($Help) {
|
||||
@@ -25,7 +24,7 @@ if ($Help) {
|
||||
Write-Host " -DryRun Compute feature name and paths without creating directories or files"
|
||||
Write-Host " -AllowExistingBranch Reuse an existing feature directory if it already exists"
|
||||
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the feature"
|
||||
Write-Host " -Number N Prefer a feature number (auto-corrected if its specs prefix exists)"
|
||||
Write-Host " -Number N Specify branch number manually (overrides auto-detection)"
|
||||
Write-Host " -Timestamp Use timestamp prefix (YYYYMMDD-HHMMSS) instead of sequential numbering"
|
||||
Write-Host " -Help Show this help message"
|
||||
Write-Host ""
|
||||
@@ -68,44 +67,11 @@ function Get-HighestNumberFromSpecs {
|
||||
return $highest
|
||||
}
|
||||
|
||||
function Test-SpecPrefixInUse {
|
||||
param(
|
||||
[string]$SpecsDir,
|
||||
[string]$FeatureNum
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $SpecsDir -PathType Container)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
return $null -ne (Get-ChildItem -LiteralPath $SpecsDir -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -like "$FeatureNum-*" } |
|
||||
Select-Object -First 1)
|
||||
}
|
||||
|
||||
function ConvertTo-CleanBranchName {
|
||||
param([string]$Name)
|
||||
|
||||
return $Name.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
||||
}
|
||||
|
||||
function Get-FittedBranchName {
|
||||
param(
|
||||
[string]$FeatureNum,
|
||||
[string]$BranchSuffix
|
||||
)
|
||||
|
||||
$fittedName = "$FeatureNum-$BranchSuffix"
|
||||
if ($fittedName.Length -gt $maxBranchLength) {
|
||||
$prefixLength = $FeatureNum.Length + 1
|
||||
$maxSuffixLength = $maxBranchLength - $prefixLength
|
||||
$truncatedSuffix = $BranchSuffix.Substring(0, [Math]::Min($BranchSuffix.Length, $maxSuffixLength))
|
||||
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
|
||||
$fittedName = "$FeatureNum-$truncatedSuffix"
|
||||
}
|
||||
|
||||
return $fittedName
|
||||
}
|
||||
# Load common functions (includes Get-RepoRoot and Resolve-Template)
|
||||
. "$PSScriptRoot/common.ps1"
|
||||
|
||||
@@ -210,40 +176,26 @@ if ($Timestamp) {
|
||||
}
|
||||
|
||||
$featureNum = ('{0:000}' -f $resolvedNumber)
|
||||
|
||||
# Treat an explicit number as a preference when its prefix is already used
|
||||
# by a feature directory. Auto-detected numbers are already conflict-free.
|
||||
$specConflict = $false
|
||||
if ($hasNumber -and (Test-Path -LiteralPath $specsDir -PathType Container)) {
|
||||
$requestedBranchName = Get-FittedBranchName -FeatureNum $featureNum -BranchSuffix $branchSuffix
|
||||
$requestedDir = Join-Path $specsDir $requestedBranchName
|
||||
if (-not $AllowExistingBranch -or -not (Test-Path -LiteralPath $requestedDir -PathType Container)) {
|
||||
$specConflict = Test-SpecPrefixInUse -SpecsDir $specsDir -FeatureNum $featureNum
|
||||
}
|
||||
}
|
||||
|
||||
if ($specConflict) {
|
||||
$requestedNum = $featureNum
|
||||
$highestNumber = Get-HighestNumberFromSpecs -SpecsDir $specsDir
|
||||
$resolvedNumber = $highestNumber
|
||||
do {
|
||||
if ($resolvedNumber -eq [long]::MaxValue) {
|
||||
Write-Error "Error: feature number must be between 0 and $([long]::MaxValue), got '9223372036854775808'"
|
||||
exit 1
|
||||
}
|
||||
$resolvedNumber++
|
||||
$featureNum = ('{0:000}' -f $resolvedNumber)
|
||||
} while (Test-SpecPrefixInUse -SpecsDir $specsDir -FeatureNum $featureNum)
|
||||
[Console]::Error.WriteLine("[specify] Warning: -Number $requestedNum conflicts with an existing spec directory; using $featureNum instead")
|
||||
}
|
||||
|
||||
$branchName = "$featureNum-$branchSuffix"
|
||||
}
|
||||
|
||||
# GitHub enforces a 244-byte limit on branch names
|
||||
# Validate and truncate if necessary
|
||||
$originalBranchName = "$featureNum-$branchSuffix"
|
||||
$branchName = Get-FittedBranchName -FeatureNum $featureNum -BranchSuffix $branchSuffix
|
||||
if ($branchName -ne $originalBranchName) {
|
||||
$maxBranchLength = 244
|
||||
if ($branchName.Length -gt $maxBranchLength) {
|
||||
# Calculate how much we need to trim from suffix
|
||||
# Account for prefix length: timestamp (15) + hyphen (1) = 16, or sequential (3) + hyphen (1) = 4
|
||||
$prefixLength = $featureNum.Length + 1
|
||||
$maxSuffixLength = $maxBranchLength - $prefixLength
|
||||
|
||||
# Truncate suffix
|
||||
$truncatedSuffix = $branchSuffix.Substring(0, [Math]::Min($branchSuffix.Length, $maxSuffixLength))
|
||||
# Remove trailing hyphen if truncation created one
|
||||
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
|
||||
|
||||
$originalBranchName = $branchName
|
||||
$branchName = "$featureNum-$truncatedSuffix"
|
||||
|
||||
[Console]::Error.WriteLine("[specify] Warning: Branch name exceeded GitHub's 244-byte limit")
|
||||
[Console]::Error.WriteLine("[specify] Original: $originalBranchName ($($originalBranchName.Length) bytes)")
|
||||
[Console]::Error.WriteLine("[specify] Truncated to: $branchName ($($branchName.Length) bytes)")
|
||||
@@ -262,16 +214,13 @@ if (-not $DryRun) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
$needsSpec = -not (Test-Path -PathType Leaf $specFile)
|
||||
$content = $null
|
||||
if ($needsSpec) {
|
||||
$content = Resolve-TemplateContent -TemplateName 'spec-template' -RepoRoot $repoRoot
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $featureDir -Force | Out-Null
|
||||
|
||||
if ($needsSpec) {
|
||||
if ($null -ne $content) {
|
||||
if (-not (Test-Path -PathType Leaf $specFile)) {
|
||||
$template = Resolve-Template -TemplateName 'spec-template' -RepoRoot $repoRoot
|
||||
if ($template -and (Test-Path $template)) {
|
||||
# Read the template content and write it to the spec file with UTF-8 encoding without BOM
|
||||
$content = [System.IO.File]::ReadAllText($template)
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($specFile, $content, $utf8NoBom)
|
||||
} else {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
param(
|
||||
[Parameter(Position=0)]
|
||||
[string]$TemplateName,
|
||||
[switch]$Json,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if ($Help) {
|
||||
Write-Output "Usage: resolve-template.ps1 <template-name> [-Json]"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not $TemplateName) {
|
||||
[Console]::Error.WriteLine("ERROR: Template name is required")
|
||||
exit 1
|
||||
}
|
||||
|
||||
. "$PSScriptRoot/common.ps1"
|
||||
|
||||
$repoRoot = Get-RepoRoot
|
||||
$templateContent = Resolve-TemplateContent -TemplateName $TemplateName -RepoRoot $repoRoot
|
||||
if ($null -eq $templateContent) {
|
||||
[Console]::Error.WriteLine("ERROR: Could not resolve required $TemplateName from the template override stack for $repoRoot")
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($Json) {
|
||||
[PSCustomObject]@{
|
||||
TEMPLATE_NAME = $TemplateName
|
||||
TEMPLATE_CONTENT = $templateContent
|
||||
} | ConvertTo-Json -Compress
|
||||
} else {
|
||||
[Console]::Out.Write($templateContent)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user