* docs: add manual testing guide for slash command validation
Adds a top-level TESTING.md that describes the manual test process PR
submitters must follow when their changes affect slash commands.
Includes:
- Process overview (identify affected commands, setup, run, report)
- Local setup instructions using editable install
- Reporting template for PR submissions
- Agent prompt that analyzes changed files and determines which
commands need testing, including transitive script dependencies
and extension hook mappings
* Update TESTING.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update TESTING.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add AIDE, Extensify, and Presetify to community extensions
Add three extensions from the mnriem/spec-kit-extensions repository:
- AI-Driven Engineering (AIDE): structured 7-step workflow for building
new projects from scratch with AI assistants
- Extensify: create and validate extensions and extension catalogs
- Presetify: create and validate presets and preset catalogs
Updates both the README community extensions table and
catalog.community.json with entries in alphabetical order.
* fix(tests): isolate preset search test from community catalog growth
Mock get_active_catalogs to return only the default catalog entry so
the test uses only its own cached data and won't break as the
community preset catalog grows.
- Add 🎨 Community Presets section between Community Extensions and Community Walkthroughs
- Add ToC entry for the new section
- Populate presets/catalog.community.json with pirate and aide-in-place presets
- Entries alphabetized: catalog by id, README table by name
- Add 🧩 Community Extensions section to README.md before Community Walkthroughs
- Add table of contents entry for the new section
- Replace extensions/README.md table with a link back to the main README
- Update EXTENSION-PUBLISHING-GUIDE.md references to point to README.md
- Update EXTENSION-DEVELOPMENT-GUIDE.md references to point to README.md
* docs(readme): consolidate Community Friends sections and fix ToC anchors
- Merge duplicate 🤝 Community Friends section (table format near bottom) into
the existing 🛠️ Community Friends section (bullet list)
- Add cc-sdd entry alongside Spec Kit Assistant
- Update intro text to 'Community projects that extend, visualize, or build on Spec Kit'
- Fix ToC anchors for Video Overview and Community Friends (remove variation selector from fragment)
* docs(readme): remove stale ToC entry for deleted Community Friends section
* fix(commands): rename NFR references to success criteria in analyze and clarify
* fix(analyze): align Success Criteria description and inventory keys with spec template
- Reword "non-functional targets" to "measurable outcomes" to match the spec template's broader scope (performance, user success, business impact)
- Use explicit FR-/SC- identifiers as primary stable keys in the requirements inventory instead of derived slugs alone
* Add Community Friends section with cc-sdd
Adds a new "Community Friends" section to the README for projects that
extend or build on Spec Kit. Starts with cc-sdd, a Claude Code plugin
that layers composable traits (quality gates, worktree isolation, agent
teams) on top of Spec Kit's core workflow.
Suggested by @mnriem in discussion #1889.
Assisted-By: 🤖 Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update cc-sdd repo URL after rename
Assisted-By: 🤖 Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Mention Superpowers explicitly in cc-sdd description
Assisted-By: 🤖 Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(scripts): prioritize .specify over git for repo root detection
When spec-kit is initialized in a subdirectory that doesn't have its
own .git, but a parent directory does, spec-kit was incorrectly using
the parent's git repository root. This caused specs to be created in
the wrong location.
The fix changes repo root detection to prioritize .specify directory
over git rev-parse, ensuring spec-kit respects its own initialization
boundary rather than inheriting a parent git repo.
Fixes#1932
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address code review feedback
- Normalize paths in find_specify_root to prevent infinite loop with relative paths
- Use -PathType Container in PowerShell to only match .specify directories
- Improve has_git/Test-HasGit to check git command availability and validate work tree
- Handle git worktrees/submodules where .git can be a file
- Remove dead fallback code in create-new-feature scripts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: check .specify before termination in find_specify_root
Fixes edge case where project root is at filesystem root (common in
containers). The loop now checks for .specify before checking the
termination condition.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: scope git operations to spec-kit root & remove unused helpers
- get_current_branch now uses has_git check and runs git with -C to
prevent using parent git repo branch names in .specify-only projects
- Same fix applied to PowerShell Get-CurrentBranch
- Removed unused find_repo_root() from create-new-feature.sh
- Removed unused Find-RepositoryRoot from create-new-feature.ps1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use cd -- to handle paths starting with dash
Prevents cd from interpreting directory names like -P or -L as options.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: check git command exists before calling get_repo_root in has_git
Avoids unnecessary work when git isn't installed since get_repo_root
may internally call git rev-parse.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(powershell): use LiteralPath and check git before Get-RepoRoot
- Use -LiteralPath in Find-SpecifyRoot to handle paths with wildcard
characters ([, ], *, ?)
- Check Get-Command git before calling Get-RepoRoot in Test-HasGit to
avoid unnecessary work when git isn't installed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(powershell): use LiteralPath for .git check in Test-HasGit
Prevents Test-Path from treating wildcard characters in paths as globs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(powershell): use LiteralPath in Get-RepoRoot fallback
Prevents Resolve-Path from treating wildcard characters as patterns.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add the AIDE extension demo to the community projects section,
showcasing a Spring Boot + React project that uses a custom
extension with an alternative spec-driven workflow featuring
a 7-step iterative lifecycle.
None of the yaml.dump() calls specify allow_unicode=True, causing
non-ASCII characters in extension descriptions to be escaped to
\uXXXX sequences in generated .agent.md frontmatter and config files.
Add allow_unicode=True to all 6 yaml.dump() call sites, and
encoding="utf-8" to all corresponding write_text() and read_text()
calls to ensure consistent UTF-8 handling across platforms.
* feat(cli): embed core pack in wheel + offline-first init (#1711, #1752)
Bundle templates, commands, and scripts inside the specify-cli wheel so
that `specify init` works without any network access by default.
Changes:
- pyproject.toml: add hatchling force-include for core_pack assets; bump
version to 0.2.1
- __init__.py: add _locate_core_pack(), _generate_agent_commands() (Python
port of generate_commands() shell function), and scaffold_from_core_pack();
modify init() to scaffold from bundled assets by default; add --from-github
flag to opt back in to the GitHub download path
- release.yml: build wheel during CI release job
- create-github-release.sh: attach .whl as a release asset
- docs/installation.md: add Enterprise/Air-Gapped Installation section
- README.md: add Option 3 enterprise install with accurate offline story
Closes#1711
Addresses #1752
* fix(tests): update kiro alias test for offline-first scaffold path
* feat(cli): invoke bundled release script at runtime for offline scaffold
- Embed release scripts (bash + PowerShell) in wheel via pyproject.toml
- Replace Python _generate_agent_commands() with subprocess invocation of
the canonical create-release-packages.sh, guaranteeing byte-for-byte
parity between 'specify init --offline' and GitHub release ZIPs
- Fix macOS bash 3.2 compat in release script: replace cp --parents,
local -n (nameref), and mapfile with POSIX-safe alternatives
- Fix _TOML_AGENTS: remove qwen (uses markdown per release script)
- Rename --from-github to --offline (opt-in to bundled assets)
- Add _locate_release_script() for cross-platform script discovery
- Update tests: remove bash 4+/GNU coreutils requirements, handle
Kimi directory-per-skill layout, 576 tests passing
- Update CHANGELOG and docs/installation.md
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(offline): error out if --offline fails instead of falling back to network
- _locate_core_pack() docstring now accurately describes that it only
finds wheel-bundled core_pack/; source-checkout fallback lives in callers
- init() --offline + no bundled assets now exits with a clear error
(previously printed a warning and silently fell back to GitHub download)
- init() scaffold failure under --offline now exits with an error
instead of retrying via download_and_extract_template
Addresses reviewer comment: https://github.com/github/spec-kit/pull/1803
* fix(offline): address PR review comments
- fix(shell): harden validate_subset against glob injection in case patterns
- fix(shell): make GENRELEASES_DIR overridable via env var for test isolation
- fix(cli): probe pwsh then powershell on Windows instead of hardcoding pwsh
- fix(cli): remove unreachable fallback branch when --offline fails
- fix(cli): improve --offline error message with common failure causes
- fix(release): move wheel build step after create-release-packages.sh
- fix(docs): add --offline to installation.md air-gapped example
- fix(tests): remove unused genreleases_dir param from _run_release_script
- fix(tests): rewrite parity test to run one agent at a time with isolated
temp dirs, preventing cross-agent interference from rm -rf
* fix(offline): address second round of review comments
- fix(shell): replace case-pattern membership with explicit loop + == check
for unambiguous glob-safety in validate_subset()
- fix(cli): require pwsh (PowerShell 7) only; drop powershell (PS5) fallback
since the bundled script uses #requires -Version 7.0
- fix(cli): add bash and zip preflight checks in scaffold_from_core_pack()
with clear error messages if either is missing
- fix(build): list individual template files in pyproject.toml force-include
to avoid duplicating templates/commands/ in the wheel
* fix(offline): address third round of review comments
- Add 120s timeout to subprocess.run in scaffold_from_core_pack to prevent
indefinite hangs during offline scaffolding
- Add test_pyproject_force_include_covers_all_templates to catch missing
template files in wheel bundling
- Tighten kiro alias test to assert specific scaffold path (download vs offline)
* fix(offline): address Copilot review round 4
- fix(offline): use handle_vscode_settings() merge for --here --offline
to prevent data loss on existing .vscode/settings.json
- fix(release): glob wheel filename in create-github-release.sh instead
of hardcoding version, preventing upload failures on version mismatch
- docs(release): add comment noting pyproject.toml version is synced by
release-trigger.yml before the tag is pushed
* fix(offline): address review round 5 + offline bundle ZIP
- fix(offline): pwsh-only, no powershell.exe fallback; clarify error message
- fix(offline): tighten _has_bundled to check scripts dir for source checkouts
- feat(release): build specify-bundle-v*.zip with all deps at release time
- feat(release): attach offline bundle ZIP to GitHub release assets
- docs: simplify air-gapped install to single ZIP download from releases
- docs: add Windows PowerShell 7+ (pwsh) requirement note
* fix(tests): session-scoped scaffold cache + timeout + dead code removal
- Add timeout=300 and returncode check to _run_release_script() to fail
fast with clear output on script hangs or failures
- Remove unused import specify_cli, _SOURCE_TEMPLATES, bundled_project fixture
- Add session-scoped scaffolded_sh/scaffolded_ps fixtures that scaffold
once per agent and reuse the output directory across all invariant tests
- Reduces test_core_pack_scaffold runtime from ~175s to ~51s (3.4x faster)
- Parity tests still scaffold independently for isolation
* fix(offline): remove wheel from release, update air-gapped docs to use pip download
* fix(tests): handle codex skills layout and iflow agent in scaffold tests
Codex now uses create_skills() with hyphenated separator (speckit-plan/SKILL.md)
instead of generate_commands(). Update _SKILL_AGENTS, _expected_ext, and
_list_command_files to handle both codex ('-') and kimi ('.') skill agents.
Also picks up iflow as a new testable agent automatically via AGENT_CONFIG.
* fix(offline): require wheel core_pack for --offline, remove source-checkout fallback
--offline now strictly requires _locate_core_pack() to find the wheel's
bundled core_pack/ directory. Source-checkout fallbacks are no longer
accepted at the init() level — if core_pack/ is missing, the CLI errors
out with a clear message pointing to the installation docs.
scaffold_from_core_pack() retains its internal source-checkout fallbacks
so parity tests can call it directly from a source checkout.
* fix(offline): remove stale [Unreleased] CHANGELOG section, scope httpx.Client to download path
- Remove entire [Unreleased] section — CHANGELOG is auto-generated at release
- Move httpx.Client into use_github branch with context manager so --offline
path doesn't allocate an unused network client
* fix(offline): remove dead --from-github flag, fix typer.Exit handling, add page templates validation
- Remove unused --from-github CLI option and docstring example
- Add (typer.Exit, SystemExit) re-raise before broad except Exception
to prevent duplicate error panel on offline scaffold failure
- Validate page templates directory exists in scaffold_from_core_pack()
to fail fast on incomplete wheel installs
- Fix ruff lint: remove unused shutil import, remove f-prefix on
strings without placeholders in test_core_pack_scaffold.py
* docs(offline): add v0.6.0 deprecation notice with rationale
- Help text: note bundled assets become default in v0.6.0
- Docstring: explain why GitHub download is being retired (no network
dependency, no proxy/firewall issues, guaranteed version match)
- Runtime nudge: when bundled assets are available but user takes the
GitHub download path, suggest --offline with rationale
- docs/installation.md: add deprecation notice with full rationale
* fix(offline): allow --offline in source checkouts, fix CHANGELOG truncation
- Simplify use_github logic: use_github = not offline (let
scaffold_from_core_pack handle fallback to source-checkout paths)
- Remove hard-fail when core_pack/ is absent — scaffold_from_core_pack
already falls back to repo-root templates/scripts/commands
- Fix truncated 'skill…' → 'skills' in CHANGELOG.md
* fix(offline): sandbox GENRELEASES_DIR and clean up on failure
- Pin GENRELEASES_DIR to temp dir in scaffold_from_core_pack() so a
user-exported value cannot redirect output or cause rm -rf outside
the sandbox
- Clean up partial project directory on --offline scaffold failure
(same behavior as the GitHub-download failure path)
* fix(tests): use shutil.which for bash discovery, add ps parity tests
- _find_bash() now tries shutil.which('bash') first so non-standard
install locations (Nix, custom CI images) are found
- Parametrize parity test over both 'sh' and 'ps' script types to
ensure PowerShell variant stays byte-for-byte identical to release
script output (353 scaffold tests, 810 total)
* fix(tests): parse pyproject.toml with tomllib, remove unused fixture
- Use tomllib to parse force-include keys from the actual TOML table
instead of raw substring search (avoids false positives)
- Remove unused source_template_stems fixture from
test_scaffold_command_dir_location
* fix: guard GENRELEASES_DIR against unsafe values, update docstring
- Add safety check in create-release-packages.sh: reject empty, '/',
'.', '..' values for GENRELEASES_DIR before rm -rf
- Strip trailing slash to avoid path surprises
- Update scaffold_from_core_pack() docstring to accurately describe
all failure modes (not just 'assets not found')
* fix: harden GENRELEASES_DIR guard, cache parity tests, safe iterdir
- Reject '..' path segments in GENRELEASES_DIR to prevent traversal
- Session-cache both scaffold and release-script results in parity
tests — runtime drops from ~74s to ~45s (40% faster)
- Guard cmd_dir.iterdir() in assertion message against missing dirs
* fix(tests): exclude YAML frontmatter source metadata from path rewrite check
The codex and kimi SKILL.md files have 'source: templates/commands/...'
in their YAML frontmatter — this is provenance metadata, not a runtime
path that needs rewriting. Strip frontmatter before checking for bare
scripts/ and templates/ paths.
* fix(offline): surface scaffold failure detail in error output
When --offline scaffold fails, look up the tracker's 'scaffold' step
detail and print it alongside the generic error message so users see
the specific root cause (e.g. missing zip/pwsh, script stderr).
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* docs: update publishing guide with Category and Effect columns
The README table now has Category and Effect columns (added in #1897),
but the publishing guide template still showed the old 3-column format.
Update to match so extension authors know to include both fields.
Made-with: Cursor
* docs: copilot comments
* chore: bump version to 0.3.2
* fix: correct changelog generation — use tag sort instead of git describe, remove duplicate entries
- Replace git describe --tags --abbrev=0 with git tag --sort=-version:refname
to find the correct previous tag (git describe misses tags on unmerged
release branches)
- Change changelog section heading from '### Changed' to '### Changes'
- Remove duplicate entries from 0.3.2 that belonged to prior releases
- Clean up changelog preamble and stale entries
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(extensions): add verify-tasks extension to community catalog
- Extension ID: verify-tasks
- Version: 1.0.0
- Detects phantom completions: tasks marked [X] in tasks.md with no real implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Replace email with name in verify-tasks catalog entry
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(presets): add enable/disable toggle and update semantics
Add preset enable/disable CLI commands and update semantics to match
the extension system capabilities.
Changes:
- Add `preset enable` and `preset disable` CLI commands
- Add `restore()` method to PresetRegistry for rollback scenarios
- Update `get()` and `list()` to return deep copies (prevents mutation)
- Update `list_by_priority()` to filter disabled presets by default
- Add input validation to `restore()` for defensive programming
- Add 16 new tests covering all functionality and edge cases
Closes#1851Closes#1852
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address PR review - deep copy and error message accuracy
- Fix error message in restore() to match actual validation ("dict" not "non-empty dict")
- Use copy.deepcopy() in restore() to prevent caller mutation
- Apply same fixes to ExtensionRegistry for parity
- Add /defensive-check command for pre-PR validation
- Add tests for restore() validation and deep copy behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* revert: remove defensive-check command from PR
* fix: address PR review - clarify messaging and add parity
- Add note to enable/disable output clarifying commands/skills remain active
- Add include_disabled parameter to ExtensionRegistry.list_by_priority for parity
- Add tests for extension disabled filtering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address PR review - disabled extension resolution and corrupted entries
- Fix _get_all_extensions_by_priority to use include_disabled=True for tracking
registered IDs, preventing disabled extensions from being picked up as
unregistered directories
- Add corrupted entry handling to get() - returns None for non-dict entries
- Add integration tests for disabled extension template resolution
- Add tests for get() corrupted entry handling in both registries
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: handle corrupted registry in list() methods
- Add defensive handling to list() when presets/extensions is not a dict
- Return empty dict instead of crashing on corrupted registry
- Apply same fix to both PresetRegistry and ExtensionRegistry for parity
- Add tests for corrupted registry handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: validate top-level registry structure in get() and restore()
- get() now validates self.data["presets/extensions"] is a dict before accessing
- restore() ensures presets/extensions dict exists before writing
- Prevents crashes when registry JSON is parseable but has corrupted structure
- Applied same fixes to both PresetRegistry and ExtensionRegistry for parity
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: validate root-level JSON structure in _load() and is_installed()
- _load() now validates json.load() result is a dict before returning
- is_installed() validates presets/extensions is a dict before checking membership
- Prevents crashes when registry file is valid JSON but wrong type (e.g., array)
- Applied same fixes to both registries for parity
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: normalize presets/extensions field in _load()
- _load() now normalizes the presets/extensions field to {} if not a dict
- Makes corrupted registries recoverable for add/update/remove operations
- Applied same fix to both registries for parity
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use raw registry keys to track corrupted extensions
- Use registry.list().keys() instead of list_by_priority() for tracking
- Corrupted entries are now treated as tracked, not picked up as unregistered
- Tighten test assertion for disabled preset resolution
- Update test to match new expected behavior for corrupted entries
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: handle None metadata in ExtensionManager.remove()
- Add defensive check for corrupted metadata in remove()
- Match existing pattern in PresetManager.remove()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add keys() method and filter corrupted entries in list()
- Add lightweight keys() method that returns IDs without deep copy
- Update list() to filter out non-dict entries (match type contract)
- Use keys() instead of list().keys() for performance
- Fix comment to reflect actual behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address defensive-check findings - deep copy, corruption guards, parity
- Extension enable/disable: use delta pattern matching presets
- add(): use copy.deepcopy(metadata) in both registries
- remove(): guard outer field for corruption in both registries
- update(): guard outer field for corruption in both registries
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: deep copy updates in update() to prevent caller mutation
Both PresetRegistry.update() and ExtensionRegistry.update() now deep
copy the input updates/metadata dict to prevent callers from mutating
nested objects after the call.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add `iflow` as a supported AI agent (the key users pass to --ai) across
all relevant configuration files, release scripts, agent context
scripts, and README. Includes consistency tests following the same
pattern as kimi/tabnine additions.
- README: describe `check` generically (git + all AGENT_CONFIG CLI agents)
- README: describe `--ai` with reference to AGENT_CONFIG for full list
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(commands): wire before/after hook events into specify and plan templates
Replicates the hook evaluation pattern from tasks.md and implement.md
(introduced in PR #1702) into the specify and plan command templates.
This completes the hook lifecycle across all SDD phases.
Changes:
- specify.md: Add before_specify/after_specify hook blocks
- plan.md: Add before_plan/after_plan hook blocks
- EXTENSION-API-REFERENCE.md: Document new hook events
- EXTENSION-USER-GUIDE.md: List all available hook events
Fixes#1788
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Mark before_commit/after_commit as planned in extension docs
These hook events are defined in the API reference but not yet wired
into any core command template. Marking them as planned rather than
removing them, since the infrastructure supports them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix hook enablement to default true when field is absent
Matches HookExecutor.get_hooks_for_event() semantics where
hooks without an explicit enabled field are treated as enabled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(docs): mark commit hooks as planned in user guide config example
The yaml config comment listed before_commit/after_commit as
"Available events" but they are not yet wired into core templates.
Moved them to a separate "Planned" line, consistent with the
API reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(commands): align enabled-filtering semantics across all hook templates
tasks.md and implement.md previously said "Filter to only hooks where
enabled: true", which would skip hooks that omit the enabled field.
Updated to match specify.md/plan.md and HookExecutor's h.get('enabled', True)
behavior: filter out only hooks where enabled is explicitly false.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs(catalog): add speckit-utils to community catalog
Adds SDD Utilities extension (resume, doctor, validate) to the
community catalog and README table. Hosted at mvanhorn/speckit-utils.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Bump catalog updated_at to current date
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
* docs: add Extensions & Presets section to README
Add a new 'Making Spec Kit Your Own: Extensions & Presets' section that covers:
- Layering diagram (Mermaid) showing resolution order
- Extensions: what they are, when to use, examples
- Presets: what they are, when to use, examples
- When-to-use-which comparison table
- Links to extensions/README.md and presets/README.md
* docs: clarify project-local overrides in layering diagram
Address review feedback: explain the project-local overrides layer
shown in the diagram, and adjust the intro to acknowledge it as a
third customization mechanism alongside extensions and presets.
* docs: Clarify template vs command resolution in README
- Separate template resolution (top-down, first-match-wins stack) from
command registration (written directly into agent directories)
- Update Mermaid diagram paths to use <preset-id> and <ext-id>
placeholders consistent with existing documentation
Addresses PR review feedback on #1898.
* docs: Clarify install-time vs runtime resolution for commands and templates
- README: label templates as runtime-resolved (stack walk) and commands
as install-time (copied into agent directories, last-installed wins)
- presets/README: add runtime note to template resolution, contrast with
install-time command registration
* docs: Address review — fix template copy wording, tighten command override description
- presets/README: clarify that preset files are copied at install but
template resolution still walks the stack at runtime
- README: describe priority-based command resolution and automatic
restoration on removal instead of vague 'replacing whatever was there'
The $Number (Int32) parameter was implicitly receiving positional
arguments intended for $FeatureDescription, causing a
ParameterBindingArgumentTransformationException when AI agents
called the script with positional strings.
Add [Parameter(Position = 0)] to $FeatureDescription so it binds
first, and mark $Number with [Parameter()] (no Position) so it
only binds by name (-Number N).
Fixes#1879
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(scripts): encode residual control chars as \uXXXX instead of stripping
json_escape() was silently deleting control characters (U+0000-U+001F)
that were not individually handled (\n, \t, \r, \b, \f). Per RFC 8259,
these must be encoded as \uXXXX sequences to preserve data integrity.
Replace the tr -d strip with a char-by-char loop that emits proper
\uXXXX escapes for any remaining control characters.
* fix(scripts): address Copilot review on json_escape control char loop
- Set LC_ALL=C for the entire loop (not just printf) so that ${#s} and
${s:$i:1} operate on bytes deterministically across locales
- Fix comment: U+0000 (NUL) cannot exist in bash strings, range is
U+0001-U+001F; adjust code guard accordingly (code >= 1)
- Emit directly to stdout instead of accumulating in a variable,
avoiding quadratic string concatenation on longer inputs
* perf(scripts): use printf -v to avoid subshell in json_escape loop
Replace code=$(printf ...) with printf -v code to assign the character
code without spawning a subshell on every byte, reducing overhead for
longer inputs.
* feat(ai): add native support for Pi coding agent by pi+gpt 5.4
* docs(pi): document MCP limitations for Pi agent
* fix: unitended kimi agent mention added to update-agent-context.ps1
* fix: address reviewer feedback
* Apply suggestions from code review
Changes in AGENTS.md weren't part of my PR, but the Copilot feedback seems to be correct is correct. I've doublechecked it with contents of test_agent_config_consistency.py and create-release-packages scripts
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(ai-skills): exclude non-speckit copilot agent markdown from skill generation
* Potential fix for pull request finding
Fix missing `.agent` filename suffix
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix test assertion speckit.plan.md to speckit.plan.agent
Fix test assertion speckit.plan.md to speckit.plan.agent
* Fix filter glob based on review suggestions
fix(ai-skills): normalize Copilot .agent template names and align template fallback filtering
* Add template glob for fallback directory
* GH Copilot Suggestions
Clarify comment regarding Copilot's use of templates in tests.
Add extra test assertion
* fix(ai-skills): normalize Copilot .agent templates and preserve fallback behavior
fix(ai-skills): handle Copilot .agent templates and fallback filtering
Normalize Copilot command template names by stripping the .agent suffix
when deriving skill names and metadata sources, so files like
speckit.plan.agent.md produce speckit-plan and map to plan.md metadata.
Also align Copilot template discovery with speckit.* filtering while
preserving fallback to templates/commands/ when .github/agents contains
only user-authored markdown files, and add regression coverage for both
non-speckit agent exclusion and fallback behavior.
* fix(ai-skills): ignore non-speckit markdown commands
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add Trae IDE support as a new agent
Add Trae (https://www.trae.ai/) as a supported AI agent in spec-kit.
Trae is an IDE-based agent that uses .trae/rules/ directory for
project-level rules in Markdown format.
Changes across 9 files:
- src/specify_cli/__init__.py: Add trae to AGENT_CONFIG (IDE-based,
.trae/ folder, rules subdir, no CLI required)
- src/specify_cli/extensions.py: Add trae to CommandRegistrar.AGENT_CONFIGS
(.trae/rules, markdown format, .md extension)
- README.md: Add Trae to supported agents table, CLI examples, and
--ai option description
- .github/workflows/scripts/create-release-packages.sh: Add trae to
ALL_AGENTS array and build case statement
- .github/workflows/scripts/create-release-packages.ps1: Add trae to
AllAgents array and switch statement
- .github/workflows/scripts/create-github-release.sh: Add trae template
zip files to release assets
- scripts/bash/update-agent-context.sh: Add TRAE_FILE, trae case in
update function, and auto-detect block
- scripts/powershell/update-agent-context.ps1: Add TRAE_FILE, ValidateSet
entry, switch case, and auto-detect block
- tests/test_agent_config_consistency.py: Add 8 consistency tests for
trae following established kimi/tabnine patterns
* fix: correct Generate-Commands parameter names for trae in PowerShell release script
Fix incorrect parameter names in the trae case of Build-Variant:
- -Format -> -Extension
- -ArgsToken -> -ArgFormat
- -OutDir -> -OutputDir
These now match the Generate-Commands function signature and all other
agent entries in the script.
Co-authored-by: Copilot <copilot@github.com>
* Update release packaging scripts and agent docs
* Update Agent.md
* Restore format
* Adjust order
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Unused
* fix: add TRAE_FILE to update_all_existing_agents() for auto-detect support
Add missing update_if_new call for TRAE_FILE in the bash
update-agent-context.sh script's update_all_existing_agents()
function, matching the PowerShell implementation.
This ensures running the script without arguments will correctly
auto-detect and update existing Trae agent files.
* Add configuration for 'trae' in agents.py
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Refactor trae configuration test for clarity
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update update-agent-context.sh
* Fix formatting in update-agent-context.sh
---------
Co-authored-by: root <root@g340-cd52-700-60f9-5561-211-6c32.byted.org>
Co-authored-by: root <root@g340-cd52-700-c3d1-c735-796-4b9e.byted.org>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat(extensions,presets): add priority-based resolution ordering
Add priority field to extension and preset registries for deterministic
template resolution when multiple sources provide the same template.
Extensions:
- Add `list_by_priority()` method to ExtensionRegistry
- Add `--priority` option to `extension add` command
- Add `extension set-priority` command
- Show priority in `extension list` and `extension info`
- Preserve priority during `extension update`
- Update RFC documentation
Presets:
- Add `preset set-priority` command
- Show priority in `preset info` output
- Use priority ordering in PresetResolver for extensions
Both systems:
- Lower priority number = higher precedence (default: 10)
- Backwards compatible with legacy entries (missing priority defaults to 10)
- Comprehensive test coverage including backwards compatibility
Closes#1845Closes#1854
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address code review feedback
- list_by_priority(): add secondary sort by ID for deterministic ordering,
return deep copies to prevent mutation
- install_from_directory/zip: validate priority >= 1 early
- extension add CLI: validate --priority >= 1 before install
- PresetRegistry.update(): preserve installed_at timestamp
- Test assertions: use exact source string instead of substring match
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address additional review feedback
- PresetResolver: add fallback to directory scanning when registry is
empty/corrupted for robustness and backwards compatibility
- PresetRegistry.update(): add guard to prevent injecting installed_at
when absent in existing entry (mirrors ExtensionRegistry behavior)
- RFC: update extension list example to match actual CLI output format
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: restore defensive code and RFC descriptions lost in rebase
- Restore defensive code in list_by_priority() with .get() and isinstance check
- Restore detailed --from URL and --dev option descriptions in RFC
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add defensive code to presets list_by_priority()
- Add .get() and isinstance check for corrupted/empty registry
- Move copy import to module level (remove local import)
- Matches defensive pattern used in extensions.py
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address reviewer feedback on priority resolution
- Rename _normalize_priority to normalize_priority (public API)
- Add comprehensive tests for normalize_priority function (9 tests)
- Filter non-dict metadata entries in list_by_priority() methods
- Fix extension priority resolution to merge registered and unregistered
extensions into unified sorted list (unregistered get implicit priority 10)
- Add tests for extension priority resolution ordering (4 tests)
The key fix ensures unregistered extensions with implicit priority 10
correctly beat registered extensions with priority > 10, and vice versa.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: DRY refactor and strengthen test assertions
- Extract _get_all_extensions_by_priority() helper in PresetResolver
to eliminate duplicated extension list construction
- Add priority=10 assertion to test_legacy_extension_without_priority_field
- Add priority=10 assertion to test_legacy_preset_without_priority_field
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add isinstance(dict) checks for corrupted registry entries
Add defensive checks throughout CLI commands and manager methods
to handle cases where registry entries may be corrupted (non-dict
values). This prevents AttributeError when calling .get() on
non-dict metadata.
Locations fixed:
- __init__.py: preset/extension info, set-priority, enable/disable,
upgrade commands
- extensions.py: list_installed()
- presets.py: list_installed()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: normalize priority display to match resolution behavior
Use normalize_priority() for all priority display in CLI commands
to ensure displayed values match actual resolution behavior when
registry data is corrupted/hand-edited.
Locations fixed:
- extensions.py: list_installed()
- presets.py: list_installed(), PresetResolver
- __init__.py: preset info, extension info, set-priority commands
Also added GraphQL query for unresolved PR comments to CLAUDE.md.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: repair corrupted priority values in set-priority commands
Changed set-priority commands to check if the raw stored value is
already a valid int equal to the requested priority before skipping.
This ensures corrupted values (e.g., "high") get repaired even when
setting to the default priority (10).
Also removed CLAUDE.md that was accidentally added to the repo.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: harden registry update methods against corrupted entries
- Normalize priority when restoring during extension update to prevent
propagating corrupted values (e.g., "high", 0, negative)
- Add isinstance(dict) checks in ExtensionRegistry.update() and
PresetRegistry.update() to handle corrupted entries (string/list)
that would cause TypeError on merge
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use safe fallback for version in list_installed()
When registry entry is corrupted (non-dict), metadata becomes {} after
the isinstance check. Use metadata.get("version", manifest.version)
instead of metadata["version"] to avoid KeyError.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
In multi-remote environments, `git fetch --all` outputs messages like
"Fetching origin" to stdout. Since `check_existing_branches()` only
redirected stderr (`2>/dev/null`), the stdout output was captured by
the `$(...)` command substitution calling this function, contaminating
the branch number return value and causing arithmetic errors like
`$((10#Fetching...))`.
Fix: redirect both stdout and stderr to /dev/null (`>/dev/null 2>&1`).
* fix(scripts): harden bash scripts with escape, compat, and cleanup fixes
- common.sh: complete RFC 8259 JSON escape (\b, \f, strip control chars)
- common.sh: distinguish python3 success-empty vs failure in resolve_template
- check-prerequisites.sh: escape doc names through json_escape in fallback path
- create-new-feature.sh: remove duplicate json_escape (already in common.sh)
- create-new-feature.sh: warn on stderr when spec template is not found
- update-agent-context.sh: move nested function to top-level for bash 3.2 compat
* fix(scripts): explicit resolve_template return code and best-effort agent updates
- common.sh: resolve_template now returns 1 when no template is found,
making the "not found" case explicit instead of relying on empty stdout
- setup-plan.sh, create-new-feature.sh: add || true to resolve_template
calls so set -e does not abort on missing templates (non-fatal)
- update-agent-context.sh: accumulate errors in update_all_existing_agents
instead of silently discarding them — all agents are attempted and the
composite result is returned, matching the PowerShell equivalent behavior
* style(scripts): add clarifying comment in resolve_template preset branch
* fix(scripts): wrap python3 call in if-condition to prevent set -e abort
Move the python3 command substitution in resolve_template into an
if-condition so that a non-zero exit (e.g. invalid .registry JSON)
does not abort the function under set -e. The fallback directory
scan now executes as intended regardless of caller errexit settings.
* fix(scripts): track agent file existence before update and avoid top-level globals
- _update_if_new now records the path and sets _found_agent before calling
update_agent_file, so that failures do not cause duplicate attempts on
aliased paths (AMP/KIRO/BOB -> AGENTS_FILE) or false "no agent files
found" fallback triggers
- Remove top-level initialisation of _updated_paths and _found_agent;
they are now created exclusively inside update_all_existing_agents,
keeping the script side-effect free when sourced
* feat: add specify status command with project info, agent detection, and feature detection
* feat: add SDD artifacts check and task progress parsing to specify status
* feat: add workflow phase detection and extensions summary to specify status
* Revert "feat: add workflow phase detection and extensions summary to specify status"
This reverts commit 1afe3c52af.
* Revert "feat: add SDD artifacts check and task progress parsing to specify status"
This reverts commit 3be36f8759.
* Revert "feat: add specify status command with project info, agent detection, and feature detection"
This reverts commit 681dc46af9.
* feat: add spec-kit-status extension to community catalog
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Revert "Potential fix for pull request finding"
This reverts commit 040447be03.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Display the extension ID below the name in `specify extension list` output.
This allows users to easily copy the ID when disambiguation is needed.
Fixes#1832
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat(extensions): add reconcile and archive to community catalog
* Update extension link text and add changelogs
Normalize extension link text in extensions/README.md (replace `[@stn1slv]` with `spec-kit-archive` and `spec-kit-reconcile`) and add CHANGELOG URLs to the corresponding entries in extensions/catalog.community.json for the Archive and Reconcile extensions.
---------
Co-authored-by: Stanislav Deviatov <stn1slv@users.noreply.github.com>
* chore: bump version to 0.3.0
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The template outputs plain text `Last updated: [DATE]` but both
update-agent-context scripts only matched `**Last updated**: [DATE]`
(bold Markdown). Make the bold markers optional in the regex so the
timestamp is refreshed regardless of formatting.
Co-authored-by: easonysliu <easonysliu@tencent.com>
Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
* Add specify doctor command for project health diagnostics
* Add tests for specify doctor command
* Document specify doctor command in README
* Revert "Document specify doctor command in README"
This reverts commit c1cfd06129.
* Revert "Add tests for specify doctor command"
This reverts commit 65e12fb62b.
* Revert "Add specify doctor command for project health diagnostics"
This reverts commit d5bd93248a.
* Add doctor extension to community catalog
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Replace eval of unquoted get_feature_paths output with safe pattern:
capture into variable, check return code, then eval quoted result
- Use printf '%q' in get_feature_paths to safely emit shell assignments,
preventing injection via paths containing quotes or metacharacters
- Add json_escape() helper for printf JSON fallback paths, handling
backslash, double-quote, and control characters when jq is unavailable
- Use jq -cn for safe JSON construction with proper escaping when
available, with printf + json_escape() fallback
- Replace declare -A (bash 4+) with indexed array for bash 3.2
compatibility (macOS default)
- Use inline command -v jq check in create-new-feature.sh since it
does not source common.sh
- Guard trap cleanup against re-entrant invocation by disarming traps
at entry
- Use printf '%q' for shell-escaped branch names in user-facing output
- Return failure instead of silently returning wrong path on ambiguous
spec directory matches
- Deduplicate agent file updates via realpath to prevent multiple writes
to the same file (e.g. AGENTS.md aliased by multiple variables)
* fix: migrate Qwen Code CLI from TOML to Markdown format (#1589)
Qwen Code CLI v0.10.0 deprecated TOML format and fully switched to
Markdown as the core format for configuration and interaction files.
- Update create-release-packages.sh: generate .md files with $ARGUMENTS
instead of .toml files with {{args}} for qwen agent
- Update create-release-packages.ps1: same change for PowerShell script
- Update AGENTS.md: reflect Qwen's new Markdown format in docs and
remove Qwen from TOML format section
- Update tests/test_ai_skills.py: add commands_dir_qwen fixture and
tests covering Markdown-format skills installation for Qwen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update CommandRegistrar qwen config to Markdown format
extensions.py CommandRegistrar.AGENT_CONFIGS['qwen'] was still set to
TOML format, causing `specify extension` to write .toml files into
.qwen/commands, conflicting with Qwen Code CLI v0.10.0+ expectations.
- Change qwen format from toml to markdown
- Change qwen args from {{args}} to $ARGUMENTS
- Change qwen extension from .toml to .md
- Add test to assert qwen config is Markdown format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(commands): create extension-commands LLM playground sandbox
* update(tests): format LLM evaluation as an automated test runner
* test(commands): map extension-commands python script with timestamps
* test(commands): map extension-commands python script with timestamps
* test(commands): update TESTING.md to evaluate discovery, lint, and deploy explicitly
* test(commands): simplify execution expectations and add timestamp calculation
* fix(tests): address copilot review comments on prompt formatting and relative paths
* fix(tests): resolve copilot PR feedback regarding extension schema structure and argparse mutually exclusive groups
* feat(extensions): add core selftest utility and migrate away from manual tests sandbox
* fix(selftest): update command name array to match spec-kit validation schema
* fix(selftest): wrap arguments in quotes to support multi-word extension names
* update the command to be more meaningful
* fix: if the extension is discovery only, it should not be installable
* Address review comments for selftest documentation
* address review comments
* address review comments
* Update extensions/selftest/commands/selftest.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* feat(extensions): implement automatic updates with atomic backup/restore
- Implement automatic extension updates with download from catalog
- Add comprehensive backup/restore mechanism for failed updates:
- Backup registry entry before update
- Backup extension directory
- Backup command files for all AI agents
- Backup hooks from extensions.yml
- Add extension ID verification after install
- Add KeyboardInterrupt handling to allow clean cancellation
- Fix enable/disable to preserve installed_at timestamp by using
direct registry manipulation instead of registry.add()
- Add rollback on any update failure with command file,
hook, and registry restoration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): comprehensive name resolution and error handling improvements
- Add shared _resolve_installed_extension helper for ID/display name resolution
with proper ambiguous name handling (shows table of matches)
- Add _resolve_catalog_extension helper for catalog lookups by ID or display name
- Update enable/disable/update/remove commands to use name resolution helpers
- Fix extension_info to handle catalog errors gracefully:
- Fallback to local installed info when catalog unavailable
- Distinguish "catalog unavailable" from "not found in catalog"
- Support display name lookup for both installed and catalog extensions
- Use resolved display names in all status messages for consistency
- Extract _print_extension_info helper for DRY catalog info printing
Addresses reviewer feedback:
- Ambiguous name handling in enable/disable/update
- Catalog error fallback for installed extensions
- UX message clarity (catalog unavailable vs not found)
- Resolved ID in status messages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): properly detect ambiguous names in extension_info
The extension_info command was breaking on the first name match without
checking for ambiguity. This fix separates ID matching from name matching
and checks for ambiguity before selecting a match, consistent with the
_resolve_installed_extension() helper used by other commands.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor(extensions): add public update() method to ExtensionRegistry
Add a proper public API for updating registry metadata while preserving
installed_at timestamp, instead of directly mutating internal registry
data and calling private _save() method.
Changes:
- Add ExtensionRegistry.update() method that preserves installed_at
- Update enable/disable commands to use registry.update()
- Update rollback logic to use registry.update()
This decouples the CLI from registry internals and maintains proper
encapsulation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): safely access optional author field in extension_info
ExtensionManifest doesn't expose an author property - the author field
is optional in extension.yml and stored in data["extension"]["author"].
Use safe dict access to avoid AttributeError.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address multiple reviewer comments
- ExtensionRegistry.update() now preserves original installed_at timestamp
- Add ExtensionRegistry.restore() for rollback (entry was removed)
- Clean up wrongly installed extension on ID mismatch before rollback
- Remove unused catalog_error parameter from _print_extension_info()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): check _install_allowed for updates, preserve backup on failed rollback
- Skip automatic updates for extensions from catalogs with install_allowed=false
- Only delete backup directory on successful rollback, preserve it on failure
for manual recovery
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address reviewer feedback on update/rollback logic
- Hook rollback: handle empty backup_hooks by checking `is not None`
instead of truthiness (falsy empty dict would skip hook cleanup)
- extension_info: use resolved_installed_id for catalog lookup when
extension was found by display name (prevents wrong catalog match)
- Rollback: always remove extension dir first, then restore if backup
exists (handles case when no original dir existed before update)
- Validate extension ID from ZIP before installing, not after
(avoids side effects of installing wrong extension before rollback)
- Preserve enabled state during updates: re-apply disabled state and
hook enabled flags after successful update
- Optimize _resolve_catalog_extension: pass query to catalog.search()
instead of fetching all extensions
- update() now merges metadata with existing entry instead of replacing
(preserves fields like registered_commands when only updating enabled)
- Add tests for ExtensionRegistry.update() and restore() methods:
- test_update_preserves_installed_at
- test_update_merges_with_existing
- test_update_raises_for_missing_extension
- test_restore_overwrites_completely
- test_restore_can_recreate_removed_entry
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs(extensions): update RFC to reflect implemented status
- Change status from "Draft" to "Implemented"
- Update all Implementation Phases to show completed items
- Add new features implemented beyond original RFC:
- Display name resolution for all commands
- Ambiguous name handling with tables
- Atomic update with rollback
- Pre-install ID validation
- Enabled state preservation
- Registry update/restore methods
- Catalog error fallback
- _install_allowed flag
- Cache invalidation
- Convert Open Questions to Resolved Questions with decisions
- Add remaining Open Questions (sandboxing, signatures) as future work
- Fix table of contents links
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address third round of PR review comments
- Refactor extension_info to use _resolve_installed_extension() helper
with new allow_not_found parameter instead of duplicating resolution logic
- Fix rollback hook restoration to not create empty hooks: {} in config
when original config had no hooks section
- Fix ZIP pre-validation to handle nested extension.yml files (GitHub
auto-generated ZIPs have structure like repo-name-branch/extension.yml)
- Replace unused installed_manifest variable with _ placeholder
- Add display name to update status messages for better UX
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(extensions): address fourth round of PR review comments
Rollback fixes:
- Preserve installed_at timestamp after successful update (was reset by
install_from_zip calling registry.add)
- Fix rollback to only delete extension_dir if backup exists (avoids
destroying valid installation when failure happens before modification)
- Fix rollback to remove NEW command files created by failed install
(files that weren't in original backup are now cleaned up)
- Fix rollback to delete hooks key entirely when backup_hooks is None
(original config had no hooks key, so restore should remove it)
Cross-command consistency fix:
- Add display name resolution to `extension add` command using
_resolve_catalog_extension() helper (was only in `extension info`)
- Use resolved extension ID for download_extension() call, not original
argument which may be a display name
Security fix (fail-closed):
- Malformed catalog config (empty/missing URLs) now raises ValidationError
instead of silently falling back to built-in catalogs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(lint): address ruff linting errors and registry.update() semantics
- Remove unused import ExtensionError in extension_info
- Remove extraneous f-prefix from strings without placeholders
- Use registry.restore() instead of registry.update() for installed_at
preservation (update() always preserves existing installed_at, ignoring
our override)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Kimi Code (kimi) CLI agent support
- Register kimi in AGENT_CONFIG with folder `.kimi/`, markdown format, requires_cli=True
- Register kimi in CommandRegistrar.AGENT_CONFIGS
- Add kimi to supported agents table in AGENTS.md and README.md
- Add kimi to release packaging scripts (bash and PowerShell)
- Add kimi CLI installation to devcontainer post-create script
- Add kimi support to update-agent-context scripts (bash and PowerShell)
- Add 4 consistency tests covering all kimi integration surfaces
- Bump version to 0.1.14 and update CHANGELOG
* fix: include .specify/templates/ and real command files in release ZIPs
- Copy real command files from templates/commands/ (with speckit. prefix)
instead of generating stubs, so slash commands have actual content
- Add .specify/templates/ to every ZIP so ensure_constitution_from_template
can find constitution-template.md on init
- Add .vscode/settings.json to every ZIP
- Having 3 top-level dirs prevents the extraction flatten heuristic from
incorrectly stripping the agent config folder (.kimi/, .claude/, etc.)
- Bump version to 0.1.14.1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(kimi): use .kimi/skills/<name>/SKILL.md structure for Kimi Code CLI
Kimi Code CLI uses a skills system, not flat command files:
- Skills live in .kimi/skills/<name>/SKILL.md (project-level)
- Invoked with /skill:<name> (e.g. /skill:speckit.specify)
- Each skill is a directory containing SKILL.md with YAML frontmatter
Changes:
- AGENT_CONFIG["kimi"]["commands_subdir"] = "skills" (was "commands")
- create-release-packages.sh: new create_kimi_skills() function creates
skill directories with SKILL.md from real template content
- Bump version to 0.1.14.2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): align kimi commands_subdir assertion with skills structure
* fix: use forward slashes for tabnine path in create-release-packages.ps1
* fix: align kimi to .kimi/skills convention and fix ARGUMENTS unbound variable
* fix: address PR review comments for kimi agent support
- Fix VERSION_NO_V undefined variable in create-github-release.sh
- Restore version $1 argument handling in create-release-packages.sh
- Fix tabnine/vibe/generic cases calling undefined generate_commands
- Align roo path .roo/rules -> .roo/commands with AGENT_CONFIG
- Fix kimi extension to use per-skill SKILL.md directory structure
- Add parent mkdir before dest_file.write_text for nested paths
- Restore devcontainer tools removed by regression + add Kimi CLI
- Strengthen test_kimi_in_powershell_validate_set assertion
* fix: restore release scripts and address all PR review comments
- Restore create-release-packages.sh to original with full generate_commands/
rewrite_paths logic; add kimi case using create_kimi_skills function
- Restore create-release-packages.ps1 to original with full Generate-Commands/
Rewrite-Paths logic; add kimi case using New-KimiSkills function
- Restore create-github-release.sh to original with proper $1 argument
handling and VERSION_NO_V; add kimi zip entries
- Add test_ai_help_includes_kimi for consistency with other agents
- Strengthen test_kimi_in_powershell_validate_set to check ValidateSet
* fix: address second round of PR review comments
- Add __AGENT__ and {AGENT_SCRIPT} substitutions in create_kimi_skills (bash)
- Add __AGENT__ and {AGENT_SCRIPT} substitutions in New-KimiSkills (PowerShell)
- Replace curl|bash Kimi installer with pipx install kimi-cli in post-create.sh
* fix: align kimi skill naming and add extension registrar test
- Fix install_ai_skills() to use speckit.<cmd> naming for kimi (dot
separator) instead of speckit-<cmd>, matching /skill:speckit.<cmd>
invocation convention and packaging scripts
- Add test_kimi_in_extension_registrar to verify CommandRegistrar.AGENT_CONFIGS
includes kimi with correct dir and SKILL.md extension
* fix(test): align kimi skill name assertion with dot-separator convention
test_skills_install_for_all_agents now expects speckit.specify (dot) for
kimi and speckit-specify (hyphen) for all other agents, matching the
install_ai_skills() implementation added in the previous commit.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(extensions): support .extensionignore to exclude files during install
Add .extensionignore support so extension authors can exclude files and
folders from being copied when users run 'specify extension add'.
The file uses glob-style patterns (one per line), supports comments (#),
blank lines, trailing-slash directory patterns, and relative path matching.
The .extensionignore file itself is always excluded from the copy.
- Add _load_extensionignore() to ExtensionManager
- Integrate ignore function into shutil.copytree in install_from_directory
- Document .extensionignore in EXTENSION-DEVELOPMENT-GUIDE.md
- Add 6 tests covering all pattern matching scenarios
- Bump version to 0.1.14
* fix(extensions): use pathspec for gitignore-compatible .extensionignore matching
Replace fnmatch with pathspec.GitIgnoreSpec to get proper .gitignore
semantics where * does not cross directory boundaries. This addresses
review feedback on #1781.
Changes:
- Switch from fnmatch to pathspec>=0.12.0 (GitIgnoreSpec.from_lines)
- Normalize backslashes in patterns for cross-platform compatibility
- Distinguish directories from files for trailing-slash patterns
- Update docs to accurately describe supported pattern semantics
- Add edge-case tests: .., absolute paths, empty file, backslashes,
* vs ** boundary behavior, and ! negation
- Move changelog entry to [Unreleased] section
Several comment and documentation strings were not updated when
Mistral Vibe support was added, leaving them out of sync with the
code. This fixes:
- update-agent-context.sh: add Generic to Supports header comment
- update-agent-context.ps1: add generic to Multi-Agent header comment
- README.md: add Mistral Vibe to CLI tool-check text
* Initial plan
* feat(extensions): implement multi-catalog stack support
- Add CatalogEntry dataclass to represent catalog entries
- Add get_active_catalogs() reading SPECKIT_CATALOG_URL, project config,
user config, or built-in default stack (org-approved + community)
- Add _load_catalog_config() to parse .specify/extension-catalogs.yml
- Add _validate_catalog_url() HTTPS validation helper
- Add _fetch_single_catalog() with per-URL caching, backward-compat for DEFAULT_CATALOG_URL
- Add _get_merged_extensions() that merges all catalogs (priority wins on conflict)
- Update search() and get_extension_info() to use merged results
annotated with _catalog_name and _install_allowed
- Update clear_cache() to also remove per-URL hash cache files
- Add extension_catalogs CLI command to list active catalogs
- Add catalog add/remove sub-commands for .specify/extension-catalogs.yml
- Update extension_add to enforce install_allowed=false policy
- Update extension_search to show source catalog per result
- Update extension_info to show source catalog with install_allowed status
- Add 13 new tests covering catalog stack, merge conflict resolution,
install_allowed enforcement, and catalog metadata
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* docs: update RFC, user guide, and API reference for multi-catalog support
- RFC: replace FUTURE FEATURE section with full implementation docs,
add catalog stack resolution order, config file examples, merge
conflict resolution, and install_allowed behavior
- EXTENSION-USER-GUIDE.md: add multi-catalog section with CLI examples
for catalogs/catalog-add/catalog-remove, update catalog config docs
- EXTENSION-API-REFERENCE.md: add CatalogEntry class docs, update
ExtensionCatalog docs with new methods and result annotations,
add catalog CLI commands (catalogs, catalog add, catalog remove)
Also fix extension_catalogs command to correctly show "Using built-in
default catalog stack" when config file exists but has empty catalogs
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: remove extraneous f-string prefixes (ruff F541)
Remove f-prefix from strings with no placeholders in catalog_remove
and extension_search commands.
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* fix: address PR review feedback for multi-catalog support
- Rename 'org-approved' catalog to 'default'
- Move 'catalogs' command to 'catalog list' for consistency
- Add 'description' field to CatalogEntry dataclass
- Add --description option to 'catalog add' CLI command
- Align install_allowed default to False in _load_catalog_config
- Add user-level config detection in catalog list footer
- Fix _load_catalog_config docstring (document ValidationError)
- Fix test isolation for test_search_by_tag, test_search_by_query,
test_search_verified_only, test_get_extension_info
- Update version to 0.1.14 and CHANGELOG
- Update all docs (RFC, User Guide, API Reference)
* fix: wrap _load_catalog_config() calls in catalog_list with try/except
- Check SPECKIT_CATALOG_URL first (matching get_active_catalogs() resolution order)
- Wrap both _load_catalog_config() calls in try/except ValidationError so a
malformed config file cannot crash `specify extension catalog list` after
the active catalogs have already been printed successfully
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Add Understanding extension to community catalog
31 deterministic requirements quality metrics based on IEEE/ISO standards.
Catches ambiguity, missing testability, and structural issues before implementation.
Includes experimental energy-based ambiguity detection.
Repository: https://github.com/Testimonial/understanding
Commands: scan, validate, energy
Hook: after_tasks validation prompt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Sort README table and catalog entries alphabetically
Move Understanding extension entry between Spec Sync and V-Model
to maintain alphabetical ordering in both files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Ladislav Bihari <ladislav.bihari@statsperform.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add spec-kit-review to catalog.community.json
and the Available Community Extensions table in extensions/README.md.
Co-authored-by: Ismael <ismael.jimenez-martinez@bmw.de>
- Extension ID: fleet
- Version: 1.0.0
- Author: sharathsatish
- Description: Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases
* Add Mistral Vibe support to Spec Kit
This commit adds comprehensive support for Mistral Vibe as an AI agent in the
Spec Kit project. The integration includes:
- Added Mistral Vibe to AGENT_CONFIG with proper CLI tool configuration
- Updated README.md with Mistral Vibe in supported agents table and examples
- Modified release package scripts to generate Mistral Vibe templates
- Updated both bash and PowerShell agent context update scripts
- Added appropriate CLI help text and documentation
Mistral Vibe is now fully supported with the same level of integration as
other CLI-based agents like Claude Code, Gemini CLI, etc.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* Add Mistral Vibe support to Spec Kit
- Added Mistral Vibe (vibe) to AGENT_CONFIG with proper TOML format support
- Updated CLI help text to include vibe as a valid AI assistant option
- Added Mistral Vibe to release scripts with correct .vibe/agents/ directory structure
- Updated agent context scripts (bash and PowerShell) with proper TOML file paths
- Added Mistral Vibe to README.md supported agents table with v2.0 slash command notes
- Used correct argument syntax {{args}} for Mistral Vibe TOML configurations
Mistral Vibe is now fully integrated with the same level of support as other
CLI-based agents like Gemini and Qwen. Users can now use specify init --ai vibe
to create projects with Mistral Vibe support.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
* Add Vibe templates to GitHub release script
creation of Mistral vibe zip
* Add 'vibe' agent to release package script
* Add 'vibe' to the list of agents in create-release-packages.sh
* chore: bump version to v1.0.1 [skip ci]
* Add generic spec kit templates to release script
* chore: bump version to v1.0.2 [skip ci]
* Update project version to 0.1.5
* Add generic spec kit templates to release script
* Add 'generic' and 'qodercli' to agent list to be aligned
* Update supported agents in update-agent-context.sh to be aligned
* Update README with new AI assistant options to be aligned
* Document --ai-commands-dir option in README to be aligned
Added new option for AI commands directory in README.
* Fix formatting in README.md for init arguments to be aligned
* Update README with AI assistant options to be aligned
Added AI options to specify init arguments in README.
* Fix formatting in README.md for project-name argument
* Update expected agent types in update-agent-context.sh to be aligned
* Update agent types and usage in update-agent-context.ps1 to be aligned
* Add support for generic AI assistant configuration to be aligned
* Fix formatting in __init__.py clean space
* Update AI assistant options in init command to be aligned
* Add 'qodercli' to expected agent types to be aligned
* Remove 'vibe' case from release package script
Removed the 'vibe' case from the create-release-packages script.
* Update README.md
ok for this
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update .github/workflows/scripts/create-release-packages.ps1
ok to commit
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add commands_subdir key to Mistral Vibe configuration
* Rename specify-agents.toml to specify-agents.md
* Update scripts/bash/update-agent-context.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update .github/workflows/scripts/create-release-packages.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/specify_cli/__init__.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/specify_cli/__init__.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fix duplicate 'commands_subdir' in vibe configuration
Removed duplicate 'commands_subdir' entries for 'vibe'.
* Add support for 'vibe' command in release script
add an mkdir and generate command
* Change commands_subdir from 'commands' to 'prompts'
* Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update update-agent-context.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update create-release-packages.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update create-release-packages.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update agent list in update-agent-context.sh
Kiro
---------
Co-authored-by: Lénaïc Huard <lenaic@lhuard.fr>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: remove per-short-name number detection from specify prompt
The specify.md prompt instructed the AI to search for existing branches
filtered by the exact short-name, causing every new feature to start at
001 since no branches matched the new short-name. The underlying
create-new-feature.sh script already has correct global numbering logic
via check_existing_branches() that searches ALL branches and spec
directories.
The fix removes the AI's flawed number-detection steps and tells it to
NOT pass --number, letting the script auto-detect the next globally
available number.
Closes#1744Closes#1468🤖 Generated with [Claude Code](https://claude.com/code)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: clarify --json flag requirement per Copilot review
- Rephrased step 2 to mention both --short-name and --json flags
- Added explicit note to always include the JSON flag for reliable output parsing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(extensions): add Jira Integration to community catalog
Adds the spec-kit-jira extension to the community catalog.
## Extension Details
- **Name**: Jira Integration
- **Version**: 2.1.0
- **Repository**: https://github.com/mbachorik/spec-kit-jira
## Features
- Create Jira Epics, Stories, and Issues from spec-kit artifacts
- 3-level hierarchy (Epic → Stories → Tasks) or 2-level mode
- Configurable custom field support
- Status synchronization between local tasks and Jira
## Commands
- `/speckit.jira.specstoissues` - Create Jira hierarchy from spec and tasks
- `/speckit.jira.discover-fields` - Discover Jira custom fields
- `/speckit.jira.sync-status` - Sync task completion to Jira
---
This PR was prepared with the assistance of Claude (Anthropic).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: address PR review comments
- Set created_at to catalog submission date (2026-03-05)
- Add Jira Integration to Available Community Extensions table in README
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: wire after_tasks and after_implement hook events into command templates (#1701)
The HookExecutor backend in extensions.py was fully implemented but
check_hooks_for_event() was never called by anything — the core command
templates had no instructions to check .specify/extensions.yml.
Add a final step to templates/commands/tasks.md (step 6, after_tasks) and
templates/commands/implement.md (step 10, after_implement) that instructs
the AI agent to:
- Read .specify/extensions.yml if it exists
- Filter hooks.{event} to enabled: true entries
- Evaluate any condition fields and skip non-matching hooks
- Output the RFC-specified hook message format, including
EXECUTE_COMMAND: markers for mandatory (optional: false) hooks
Bumps version to 0.1.7.
* fix: clarify hook condition handling and add YAML error guidance in templates
- Replace ambiguous "evaluate any condition value" instruction with explicit
guidance to skip hooks with non-empty conditions, deferring evaluation to
HookExecutor
- Add instruction to skip hook checking silently if extensions.yml cannot
be parsed or is invalid
* Fix/extension hooks not triggered (#1)
* feat(templates): implement before-hooks check as pre-execution phase
* test(hooks): create scenario for LLMs/Agents on hooks
---------
Co-authored-by: Dhilip <s.dhilipkumar@gmail.com>
* feat: add kiro-cli and AGENT_CONFIG consistency coverage
* fix: address PR feedback for kiro-cli migration
* test: assert init invocation result in --here mode test
* test: capture init result in here-mode command test
* chore: save local unapproved work in progress
* fix: resolve remaining PR1690 review threads
* Update src/specify_cli/__init__.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* test: reduce brittleness in ai help alias assertion
* fix: resolve PR1690 ruff syntax regression
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add retrospective extension to community catalog.
Register the new retrospective extension release so users can discover and install it from the community catalog.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add Retrospective Extension to community catalog
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: correct Copilot extension command registration (#copilot)
- Use .agent.md extension for commands in .github/agents/
- Generate companion .prompt.md files in .github/prompts/
- Clean up .prompt.md files on extension removal
- Add tests for Copilot-specific registration behavior
Bumps version to 0.1.7.
* fix: test copilot prompt cleanup via ExtensionManager.remove() instead of manual unlink
---------
Co-authored-by: Ismael <ismael.jimenez-martinez@bmw.de>
* fix(implement): remove Makefile from C ignore patterns
Makefile is typically source-controlled and should not be in .gitignore.
Replaced with proper autotools-generated files (autom4te.cache/, config.status).
Co-authored-by: Augment Agent <noreply@augmentcode.com>
* fix(implement): restore config.log in C ignore patterns
---------
Co-authored-by: Augment Agent <noreply@augmentcode.com>
* Add sync extension to community catalog
- Extension ID: sync
- Version: 0.1.0
- Author: bgervin
- Description: Detect and resolve drift between specs and implementation
* fix: use main branch in URLs per Copilot review
* Reorder community extensions table alphabetically
Per Copilot review feedback and EXTENSION-PUBLISHING-GUIDE.md
---------
Co-authored-by: Barry Gervin <bgervin@hotmail.com>
* fix(checklist): clarify file handling behavior for append vs create
Resolves contradictory instructions in checklist.md lines 97-99 where the
template stated both 'append to existing file' and 'creates a NEW file'.
Changes:
- Clarified that if file doesn't exist, create new with CHK001
- If file exists, append new items continuing from last CHK ID
- Emphasized preservation of existing content (never delete/replace)
Co-authored-by: Augment Agent <noreply@augmentcode.com>
* fix(checklist): align report language with append behavior
---------
Co-authored-by: Augment Agent <noreply@augmentcode.com>
Resolves inconsistency where line 92 stated 'Maximum of 10 total questions'
while all other references (lines 2, 91, 100, 134, 158, 178) consistently
specify a maximum of 5 questions.
Co-authored-by: Augment Agent <noreply@augmentcode.com>
* fix: use release branch + PR instead of direct push to main
Bypass branch protection rules by pushing version bump to a
chore/release-vX.Y.Z branch, tagging that commit, then opening
an auto PR to merge back into main. The release workflow still
triggers immediately from the tag push.
* fix: remove --label automated from gh pr create (label does not exist)
* fix: split release process to sync pyproject.toml version with git tags (#1721)
- Split release workflow into two: release-trigger.yml and release.yml
- release-trigger.yml: Updates pyproject.toml, generates changelog from commits, creates tag
- release.yml: Triggered by tag push, builds artifacts, creates GitHub release
- Ensures git tags point to commits with correct version in pyproject.toml
- Auto-generates changelog from commit messages since last tag
- Supports manual version input or auto-increment patch version
- Added simulate-release.sh for local testing without pushing
- Added comprehensive RELEASE-PROCESS.md documentation
- Updated pyproject.toml to v0.1.10 to sync with latest release
This fixes the version mismatch issue where tags pointed to commits with
outdated pyproject.toml versions, preventing confusion when installing from source.
* Update .github/workflows/RELEASE-PROCESS.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update .github/workflows/scripts/simulate-release.sh
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update .github/workflows/release.yml
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update .github/workflows/release-trigger.yml
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: harden release-trigger against shell injection and fix stale docs
- Pass workflow_dispatch version input via env: instead of direct
interpolation into shell script, preventing potential injection attacks
- Validate version input against strict semver regex before use
- Fix RELEASE-PROCESS.md Option 2 still referencing [Unreleased] section
handling that no longer exists in the workflow
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: prepend YAML frontmatter to Cursor .mdc files for auto-inclusion
Cursor IDE requires YAML frontmatter with `alwaysApply: true` in .mdc
rule files for them to be automatically loaded. Without this frontmatter,
users must manually configure glob patterns for the rules to take effect.
This fix adds frontmatter generation to both the bash and PowerShell
update-agent-context scripts, handling three scenarios:
- New .mdc file creation (frontmatter prepended after template processing)
- Existing .mdc file update without frontmatter (frontmatter added)
- Existing .mdc file with frontmatter (no duplication)
Closes#669🤖 Generated with [Claude Code](https://claude.com/code)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: address Copilot review suggestions
- Handle CRLF line endings in frontmatter detection (grep '^---' instead
of '^---$')
- Fix double blank line after frontmatter in PowerShell New-AgentFile
- Remove unused tempfile import from tests
- Add encoding="utf-8" to all open() calls for cross-platform safety
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: Document dual-catalog system for extensions
- Clarify distinction between catalog.json (curated) and catalog.community.json (reference)
- Update EXTENSION-DEVELOPMENT-GUIDE.md to explain community catalog submission
- Update EXTENSION-PUBLISHING-GUIDE.md with dual-catalog workflow
- Update EXTENSION-USER-GUIDE.md with catalog selection guidance
- Expand README.md with comprehensive catalog explanation
- Update RFC-EXTENSION-SYSTEM.md with dual-catalog design and current implementation
- Change GitHub references from statsperform to github
- Add SPECKIT_CATALOG_URL environment variable documentation
This clarifies how organizations can curate their own catalog while
browsing community-contributed extensions for discovery.
* Update extensions/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update extensions/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update extensions/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Register the new retrospective extension release so users can discover and install it from the community catalog.
Co-authored-by: Cursor <cursoragent@cursor.com>
**Repository**: https://github.com/dsrednicki/spec-kit-cleanup
**Version**: 1.0.0
**License**: MIT
**Author**: @dsrednicki
Adds catalog entry for the Cleanup Extension - a post-implementation
quality gate that fixes small issues (scout rule), creates tasks for
medium issues, and generates analysis for large issues.
* chore: bump version to v0.0.6 [skip ci]
* Fix parameter ordering issues in CLI (#1641)
- Add validation to detect when option flags are consumed as values
- Provide clear error messages with helpful hints and examples
- Add 5 comprehensive tests to prevent regressions
- Update CODEOWNERS to @mnriem
- Bump version to 0.1.6 with changelog entry
Fixes: #1641
* Fix ruff linting errors: remove f-string prefix from strings without placeholders
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Added commands_subdir field to AGENT_CONFIG for all agents
- Updated install_ai_skills() to use commands_subdir instead of hardcoded 'commands'
- Fixed --ai-skills flag for copilot, opencode, windsurf, codex, kilocode, q, and agy
- Bumped version to 0.1.5
- Updated AGENTS.md documentation with new field
Affected agents now correctly locate their command templates:
- copilot: .github/agents/
- opencode: .opencode/command/ (singular)
- windsurf: .windsurf/workflows/
- codex: .codex/prompts/
- kilocode: .kilocode/workflows/
- q: .amazonq/prompts/
- agy: .agent/workflows/
All 51 tests pass.
- Add --ai generic option for unsupported AI agents (bring your own agent)
- Require --ai-commands-dir to specify where agent reads commands from
- Generate Markdown commands with $ARGUMENTS format (compatible with most agents)
- Rebuild CHANGELOG from GitHub releases (last 10 releases)
- Align version to 0.1.3
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: pin click>=8.1 to prevent Python 3.14/Homebrew env isolation failures
Fixes#1631. When uv installs specify-cli on macOS with Homebrew Python
3.14, the virtual environment can fail to fully isolate from the system
site-packages, causing Homebrew's click to be loaded instead of the one
uv installed. If that system click is older than 8.1, it lacks the `ctx`
keyword argument in `ParamType.get_metavar()`, which typer 0.24.0 requires,
resulting in:
TypeError: ParamType.get_metavar() got an unexpected keyword argument 'ctx'
Adding an explicit `click>=8.1` dependency gives uv a hard constraint so
the correct version is always resolved and installed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update CHANGELOG.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add support for Antigravity (agy) agent
* fix a few things after gemini code update
* Fix missed merge conflicts
* As PR states it is IDE integration setting requires_cli to 'False'
---------
Co-authored-by: Manfred Riem <manfred.riem@microsoft.com>
* Add modular extension system for Spec Kit
Implement a complete extension system that allows third-party developers
to extend Spec Kit functionality through plugins.
## Core Features
- Extension discovery and loading from local and global directories
- YAML-based extension manifest (extension.yml) with metadata and capabilities
- Command extensions: custom slash commands with markdown templates
- Hook system: pre/post hooks for generate, task, and sync operations
- Extension catalog for discovering and installing community extensions
- SPECKIT_CATALOG_URL environment variable for catalog URL override
## Installation Methods
- Catalog install: `specify extension add <name>`
- URL install: `specify extension add <name> --from <url>`
- Dev install: `specify extension add --dev <path>`
## Implementation
- ExtensionManager class for lifecycle management (load, enable, disable)
- Support for extension dependencies and version constraints
- Configuration layering (global → project → extension)
- Hook conditions for conditional execution
## Documentation
- RFC with design rationale and architecture decisions
- API reference for extension developers
- Development guide with examples
- User guide for installing and managing extensions
- Publishing guide for the extension catalog
## Included
- Extension template for bootstrapping new extensions
- Comprehensive test suite
- Example catalog.json structure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Update Jira extension to v2.1.0 in catalog
Adds 2-level mode support (Epic → Stories only).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address PR review feedback
- Fix Zip Slip vulnerability in ZIP extraction with path validation
- Fix keep_config option to actually preserve config files on removal
- Add URL validation for SPECKIT_CATALOG_URL (HTTPS required, localhost exception)
- Add security warning when installing from custom URLs (--from flag)
- Empty catalog.json so organizations can ship their own catalogs
- Fix markdown linter errors (MD040: add language to code blocks)
- Remove redundant import and fix unused variables in tests
- Add comment explaining empty except clause for backwards compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add comprehensive organization catalog customization docs
- Explain why default catalog is empty (org control)
- Document how to create and host custom catalogs
- Add catalog JSON schema reference
- Include use cases: private extensions, curated catalogs, air-gapped environments
- Add examples for combining catalog with direct installation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix test assertions for extension system data structures
- Update test_config_backup_on_remove to use new subdirectory structure
(.backup/test-ext/file.yml instead of .backup/test-ext-file.yml)
- Update test_full_install_and_remove_workflow to handle registered_commands
being a dict keyed by agent name instead of a flat list
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address Copilot review feedback
- Fix localhost URL check to use parsed.hostname instead of netloc.startswith()
This correctly handles URLs with ports like localhost:8080
- Fix YAML indentation error in config-template.yml (line 57)
- Fix double space typo in example.md (line 172)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add catalog.example.json as reference for organizations
The main catalog.json is intentionally empty so organizations can ship
their own curated catalogs. This example file shows the expected schema
and structure for creating organization-specific catalogs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Address remaining Copilot security and logic review feedback
- Fix Zip Slip vulnerability by using relative_to() for safe path validation
- Add HTTPS validation for extension download URLs
- Backup both *-config.yml and *-config.local.yml files on remove
- Normalize boolean values to lowercase for hook condition comparisons
- Show non-default catalog warning only once per instance
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Ignoring linter for extensions directory
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Manfred Riem <manfred.riem@microsoft.com>
Moves constitution template from memory/ to templates/ to prevent
overwrites when spec-kit is reinitialized with a different AI agent.
Changes:
- Move memory/constitution.md to templates/constitution-template.md
- Update CLI to copy template to memory/ only on first initialization
- Update constitution command to reference correct paths with .specify/ prefix
- Preserve existing constitution.md when reinitializing project
The CLI now checks if .specify/memory/constitution.md exists:
- If it exists: preserve it (no overwrite)
- If it doesn't exist: copy from .specify/templates/constitution-template.md
This allows users to customize their constitution without losing changes
when adding support for additional AI agents or reinitializing.
Fixes#1541
Co-authored-by: jjoung1128 <jinwoong.joung@gmail.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add language specifiers to fenced code blocks (bash, text)
- Fix whitespace issues around code fences
- Disable MD036 (emphasis as heading) for intentional warning text
- Disable MD060 (table column style) for existing table formats
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add commands for initializing Spec Kit in existing projects directly
in the installation section. Shows both `specify init .` and
`specify init --here` options to make brownfield usage immediately
discoverable.
Fixes#1285
When --number 027 was passed, printf '%03d' interpreted it as octal,
converting 027 (octal) to 23 (decimal). Now forces base-10 with 10# prefix.
Bug: User passes --number 027, gets feature 023 instead of 027
Root cause: printf %03d treats leading zeros as octal notation
Fix: Use $((10#$BRANCH_NUMBER)) to force decimal interpretation
Example:
- Before: --number 027 → octal 027 → decimal 23 → feature 023
- After: --number 027 → decimal 27 → feature 027
Note: PowerShell version does not have this bug because [int] type
conversion in PowerShell does not treat leading zeros as octal.
- Convert blockquotes to GFM admonitions for better visibility
- Add current directory initialization example
- Expand process to 6 steps with clarify, analyze, checklist, implement
- Update example with separate clarify commands
- Add proper links in Next Steps
Fixes#906, #472
Generated-by: GitHub Copilot <github.copilot@github.com>
Signed-off-by: Ashley Childress <6563688+anchildress1@users.noreply.github.com>
The check_existing_branches (bash) and Get-NextBranchNumber (PowerShell)
functions no longer use the short_name parameter since they now find the
global maximum across ALL features. This commit:
1. Removes the unused parameter from function signatures
2. Updates all call sites to not pass the parameter
This prevents the scripts from failing when the function is called with
the wrong number of arguments.
The check_existing_branches (bash) and Get-NextBranchNumber (PowerShell)
functions were only looking for branches/specs matching the SAME short name
when determining the next feature number. This caused collisions where
multiple features could be assigned the same number if they had different
short names.
For example, if feature 023-ci-optimization existed, creating a new feature
with a different short name would incorrectly use 001 instead of 024.
This fix changes both functions to:
1. Use get_highest_from_branches() / Get-HighestNumberFromBranches to find
the highest number across ALL branches globally
2. Use get_highest_from_specs() / Get-HighestNumberFromSpecs to find the
highest number across ALL spec directories globally
3. Return the maximum of both + 1
The helper functions already existed but were not being used. This fix
properly utilizes them to ensure features are numbered sequentially
regardless of their short names.
Issue: Branch number collisions when creating features with different names
Impact: Prevents multiple features from sharing the same number prefix
description:URL to the GitHub release archive for your preset (e.g., https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip)