* refactor: create commands/ package and move init handler (PR-4/8)
- Extract agent configuration constants (AGENT_CONFIG, AI_ASSISTANT_HELP,
SCRIPT_TYPE_CHOICES, etc.) to _agent_config.py to avoid circular imports
- Create commands/ package skeleton with stub modules for each command group
- Move init command handler (~670 lines) from __init__.py to commands/init.py
using the register(app) pattern; lazy imports inside the handler body
prevent circular dependencies with __init__.py
- Re-export AGENT_CONFIG, AI_ASSISTANT_HELP, SCRIPT_TYPE_CHOICES from
__init__.py for backward compatibility
- Add tests/test_commands_package.py to verify package structure
* fix(tests): update patch targets after moving init handler to commands/init.py
_stdin_is_interactive and select_with_arrows are now bound in
specify_cli.commands.init, not specify_cli directly.
* fix(lint): remove unused imports and mark re-exports in __init__.py
- Remove shutil, shlex top-level imports (used lazily inside functions)
- Remove rich.live.Live import (moved to commands/init.py)
- Mark select_with_arrows and _locate_bundled_workflow as explicit
re-exports to satisfy ruff F401
* chore: add from __future__ import annotations to new modules
Aligns with the project convention established in _console.py, _assets.py,
_utils.py, and other modules.
* docs(cli): align init help with bundled scaffolding
Potential fix for pull request finding
Update command package documentation and init help text to reflect the current implementation: init uses bundled assets and integration setup, while placeholder command modules are import anchors until extracted. Remove the unused tracker-active flag assignment that had no reader in the codebase.
Constraint: --offline is hidden/no-op and init no longer downloads templates from GitHub releases
Rejected: Add no-op register functions to placeholder modules | would imply extracted command groups are implemented there
Confidence: high
Scope-risk: narrow
Directive: Keep CLI help text aligned with the actual init scaffolding path
Tested: uv run specify init --help; uv run pytest tests/test_commands_package.py tests/test_agent_config_consistency.py -q; uv run pytest tests/test_commands_package.py tests/test_console_imports.py tests/integrations/test_cli.py -q
Not-tested: full test suite
* fix(init): align preset failure reporting with _print_cli_warning helper
Use the _print_cli_warning helper (introduced in main) for preset install
failures so that output matches the expected format:
"Failed to install preset '<name>': ..."
"Continuing without the optional preset."
* fix(init): remove unused lazy imports
The init command imported CLI error formatting helpers through its circular-dependency-safe lazy import block, but the module does not use them. Remove those imports so ruff does not report F401.
Constraint: uvx ruff check src/ must pass.
Rejected: Wire the helpers into init error handling | Existing preset warnings already use _print_cli_warning, and changing behavior is unnecessary for this lint fix.
Confidence: high
Scope-risk: narrow
Directive: Keep lazy import blocks limited to names consumed in the importing module.
Tested: uvx ruff check src/
Not-tested: Full pytest suite
Co-authored-by: OmX <omx@oh-my-codex.dev>
---------
Co-authored-by: OmX <omx@oh-my-codex.dev>
- AGENTS.md: branch naming as a requirement for AI coding agents
- CONTRIBUTING.md: branch naming as a recommendation for human contributors
- Convention: <type>/<number>-<short-slug> where number is issue or PR number
Closes#2677
* chore: bump version to 0.8.13
* chore: begin 0.8.14.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: while/do-while loop condition reads stale iteration-0 step output
After executing namespaced loop body steps, copy each iteration's
results back to the original unprefixed step key so that
evaluate_condition() sees the latest values instead of stale
iteration-0 data.
Fixes#2592
* address review: cross-platform tests, preserve iteration-0 history
- Rewrite shell scripts in tests to use Python via script files
instead of POSIX syntax, so they pass on Windows CI.
- Snapshot iteration-0 nested-step results under a namespaced key
(parent:child:0) before the first copy-back overwrite, preserving
complete per-iteration history for debugging.
* address review: skip copy-back on paused/failed iterations
Move the status check before the copy-back so that partial results
from paused or failed nested steps (e.g., a gate awaiting input)
do not overwrite the unprefixed key. This preserves correct resume
behavior.
* address review: quote paths in test shell commands
Quote both the Python executable and script file paths in the
run: commands to handle spaces in paths on Windows.
* address review: execute loop body with original IDs
Instead of namespacing step IDs for execution and copying results
back, execute the loop body with original (unprefixed) step IDs so
results naturally land at the right keys. Snapshot previous
iteration results to namespaced keys (parent:child:N) for history
only.
This fixes multi-step loop bodies where step B references step A's
output within the same iteration — previously step B would see
stale data until the copy-back ran after the entire iteration.
* address review: namespaced execution with per-step copy-back
Revert to namespaced step IDs for execution (preserving unique
log entries and state keys per iteration) but copy each step's
result back to the unprefixed key immediately after it completes.
This preserves backward compatibility (same namespaced key format,
same log IDs) while fixing both the condition evaluation bug and
inter-step references within multi-step loop bodies.
* address review: alias after status check, add multi-step body test
- Move per-step aliasing below the PAUSED/FAILED/ABORTED status
check so partial results from incomplete steps are not aliased
back to the unprefixed key.
- Add test_while_loop_multi_step_body_inter_step_refs to exercise
a multi-step loop body where step B reads step A's output within
the same iteration, verifying per-step aliasing works correctly.
Addresses feedback from @doquanghuy (items 2 & 4) and Copilot
review on commit 9d0a222.
* address review: stable fallback IDs, expression-based inter-step test
- Use enumerate() for stable fallback IDs when loop body steps lack
an explicit id (step-0, step-1, etc. instead of always step-0).
- Rewrite multi-step body test so step B uses expression
substitution ({{ steps.step-a.output.stdout }}) instead of
reading the counter file directly, making it a true regression
test for per-step aliasing.
`bool` is a subclass of `int` in Python, so `int(True)` silently returns
`1`. The extension- and preset-catalog config readers coerced priority
with a bare `int(item.get("priority", idx + 1))`, which meant a YAML
config like:
catalogs:
- name: mine
url: https://example.com/catalog.json
priority: yes # parses to True
was silently accepted as a valid priority of 1, quietly reordering the
catalog stack instead of raising the same `Invalid priority` error a
typo of `priority: not-a-number` already raises.
The sibling integration-catalog reader in `src/specify_cli/catalogs.py`
already guards this case (see `catalogs.py:137`). This change mirrors
that pattern in `extensions.py` and `presets.py` so the three catalog
validators stay consistent, and adds regression tests for both readers
matching the existing `test_load_catalog_config_rejects_boolean_priority`
template in `tests/integrations/test_integration_catalog.py`.
* Add agentic workflows for community catalog submissions
Add GitHub Agentic Workflows that automatically process community
extension and preset submission issues:
- add-community-extension.md: triggered by extension-submission issues,
validates the submission, updates extensions/catalog.community.json
and docs/community/extensions.md, then opens a draft PR
- add-community-preset.md: parallel workflow for preset-submission
issues, updates presets/catalog.community.json and
docs/community/presets.md
Both workflows:
- Trigger on opened, edited, or labeled events (maintainers can
retroactively label pre-existing issues)
- Validate ID format, semver, repo existence, required files, release,
and submission checklists
- Label issues with validation-passed or validation-failed
- Create draft PRs with Closes #N for maintainer review
Also includes gh-aw scaffolding (.github/aw/, .gitattributes lock file
rule, dependabot ignore for gh-aw-actions).
* Suppress whitespace checks on generated .lock.yml files
These files are auto-generated by gh aw compile and contain trailing
whitespace in the ASCII art header and indented YAML blocks that we
cannot control. Add -whitespace attribute to skip git whitespace
checks on them.
* feat: add self-check tip to check output
* style: drop trailing period from self-check tip
Aligns the new tip with the other `Tip:` lines in `specify check`,
which don't end in a period. Per Copilot review feedback on #2574.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidate the CLI diagnostic plan, implementation, and test hardening into one reviewable change. The CLI now reports phase and target context for broad failure paths while preserving existing fail-fast behavior for real setup failures and warning-only behavior for optional best-effort work.
The workflow unit tests also avoid discovering real local agent CLIs, so developer machines with tools such as gemini installed do not hang pytest during metadata-only assertions.
Constraint: CLI setup failures must remain fail-fast, while optional preset and cleanup paths should continue with clear warnings.
Rejected: Replace broad handlers across the whole codebase in one pass | too broad for a targeted CLI diagnostic fix
Rejected: Add runtime timeouts to workflow agent dispatch | dispatch may legitimately be long-running and the observed hang was test isolation
Confidence: high
Scope-risk: moderate
Directive: Keep future best-effort CLI warnings tied to the failed phase and target so users can diagnose setup state.
Tested: uvx ruff check src/; uv run pytest tests/integrations/test_cli.py -v; uv run pytest tests/test_workflows.py::TestCommandStep::test_step_override_integration tests/test_workflows.py::TestPromptStep::test_execute_with_step_integration tests/test_workflows.py::TestPromptStep::test_execute_with_model -vv; uv run pytest
Not-tested: Real Nacos/PG/Redis-style external service failure injection; real interactive workflow dispatch against installed gemini CLI
* chore: bump version to 0.8.12
* chore: begin 0.8.13.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(codex): inject dot-to-hyphen hook command note in Codex skills
Hook commands in `.specify/extensions.yml` use dotted ids like
`speckit.git.commit`, but Codex skills are named with hyphens
(`speckit-git-commit`). The Claude integration handles this via an
explicit instruction injected into each generated SKILL.md by
`ClaudeIntegration.post_process_skill_content`, but the Codex
integration had no such override, so Codex would emit
`/speckit.git.commit` (which does not resolve) instead of
`/speckit-git-commit`.
This adds the same `_inject_hook_command_note` helper and a
`post_process_skill_content` override to `CodexIntegration`, plus a
small `setup()` override that applies the post-process to each
generated SKILL.md (mirroring the pattern in `ClaudeIntegration`).
Also widens the existing
`test_non_claude_post_process_is_identity` test to use `agy`
(another `SkillsIntegration` with no override), since asserting
identity behavior on Codex would now incorrectly fail.
Tests:
- New `TestCodexHookCommandNote` class mirrors
`TestClaudeHookCommandNote`: setup-level injection, no-op when
no hook block is present, idempotency, and indentation
preservation.
- `pytest tests/` → 2866 passed, 34 skipped.
Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com>
* fix(codex): handle empty eol when instruction is final line without newline
The hook-note injection regex allowed end-of-string matches via ``$``,
which left the captured ``eol`` empty. When the matched indent was also
empty, the substitution concatenated the note onto the same line as the
instruction. Default ``eol`` to ``\n`` when the capture is empty.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(workflow): support integration: auto to follow project's initialized AI
Closes#2406
(squashed)
* fix(workflow): combine JSONDecodeError and UnicodeDecodeError handling
Address Copilot feedback: UnicodeDecodeError can be raised by both
read_text() and json.loads(), so combining the handlers ensures both
cases produce a consistent, clear error message.
* fix(workflows): honor integration_state schema guard and modern state in 'integration: auto'
Three Copilot follow-ups on PR #2421:
1. engine.py:799 — `_load_project_integration` was bypassing the same
schema guard `_read_integration_json` enforces. It now reads the
schema field directly, returns None on a future schema (so the
workflow falls back to the literal 'auto' default rather than
guessing), and routes through `normalize_integration_state` /
`default_integration_key` so modern installs that record
`default_integration` / `installed_integrations` (without the
legacy top-level `integration` field) resolve correctly.
2. test_workflows.py — added two regression cases:
- `integration: auto` resolves a modern normalized state file
- `integration: auto` falls back when the state file declares a
newer `integration_state_schema` than this CLI supports
3. test_cli.py — added a CLI-level regression for the `UnicodeDecodeError`
branch in `_read_integration_json` to match the existing
malformed-JSON coverage.
* refactor(integration): extract shared try_read_integration_json helper
Address Copilot review on PR #2421:
Both `_read_integration_json` (CLI) and `_load_project_integration`
(workflow engine) were parsing `.specify/integration.json` independently,
duplicating the schema guard and risking drift between the two readers.
Extract the parse + schema validation into a single low-level helper
`try_read_integration_json` in `integration_state.py` that returns either
the normalized state or a structured `IntegrationReadError`. Both callers
now delegate to this helper:
- CLI keeps its loud-fail UX: each error kind ("decode", "os",
"not_object", "schema_too_new") is translated into the existing console
message + typer.Exit(1).
- Engine keeps its silent fallback: any error simply returns None so
`integration: auto` falls back to the workflow's literal default.
This eliminates the divergence Copilot flagged without changing observable
behavior for either caller.
* fix(integration): distinguish missing file from non-regular path
Address Copilot review on PR #2421:
`try_read_integration_json` was collapsing two distinct cases into a
single `(None, None)` return:
1. `.specify/integration.json` truly missing — silent fallback is correct.
2. Path exists but is a directory, socket, or other non-regular file —
this is a misconfiguration the CLI should surface loudly.
Split the check: `exists()` falsey returns `(None, None)`; existing-but-
not-a-regular-file returns `(None, IntegrationReadError(kind="os", ...))`
so the CLI's loud-fail path produces an actionable error while the
engine still treats it as a fallback to the workflow's literal default.
* docs(workflow): clarify version pin, advisory integrations list, enum exemption
- workflow.yml: fix comment that said 0.8.3 was first release with auto
resolution; the pin is >=0.8.5 so the comment now matches the pin.
- workflow.yml: clarify that requires.integrations.any is an advisory,
non-exhaustive compatibility hint, not a closed set.
- engine.py: clarify that the auto-sentinel exemption only skips enum
membership; declared type is still enforced through _coerce_input.
* fix(workflow): resolve auto sentinel for provided values; report stat errors
Two Copilot findings fixed:
1. _resolve_inputs only resolved the ``integration: auto`` sentinel when it
came from the input default. A caller explicitly providing
``{"integration": "auto"}`` (which the workflow prompt advertises as a
valid value) bypassed _resolve_default and the literal "auto" leaked
to dispatch. Provided values now go through the same resolution path
as defaults, and the enum-membership exemption applies in both cases.
Regression test added.
2. try_read_integration_json used Path.exists() / Path.is_file() as a
pre-check. Both return False on some OSErrors (e.g. permission errors
during stat), which silently treated an unreadable-but-present file
as missing — the engine fell back without warning and the CLI failed
to surface the loud error. The pre-check is gone: read_text() is
attempted directly, FileNotFoundError means missing (silent fallback),
IsADirectoryError and other OSErrors become loud IntegrationReadError.
* fix(workflow): enforce declared type for string inputs, reject bool-as-number
Two Copilot findings fixed:
1. _coerce_input previously coerced/validated only ``number`` and
``boolean`` types, so ``type: string`` silently accepted any Python
value (numbers, lists, dicts). A YAML authoring mistake like
``type: string`` + ``default: 5`` slipped through. Strings are now
required to actually be strings; non-strings raise ValueError, which
surfaces as an ``invalid default`` error from validate_workflow.
2. ``type: number`` accepted ``default: true`` because ``bool`` is a
subclass of ``int`` (``float(True) == 1.0``). Bools are now rejected
explicitly in the number path so the YAML mistake fails fast. The
boolean path is also tightened to reject non-bool / non-string
values for symmetry.
Comment on the auto-sentinel enum exemption updated to reflect the
stronger guarantee. Regression tests added for both rejections.
* fix(cli): drop unused normalize_integration_state import to satisfy ruff
CI's `uvx ruff check src/` flagged this as F401: the symbol was imported
under a private alias but never referenced. Tests stay green after
removal.
* chore: bump version to 0.8.11
* chore: begin 0.8.12.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* refactor: extract _version.py from __init__.py (PR-3/8)
Move version-checking helpers and `specify self` sub-commands into a
focused `_version.py` module.
Moved symbols:
- GITHUB_API_LATEST — GitHub releases API endpoint constant
- _get_installed_version — importlib.metadata-based version lookup
- _normalize_tag — strip leading 'v' from release tag strings
- _is_newer — PEP 440 version comparison
- _fetch_latest_release_tag — single outbound call to GitHub API
- self_app — Typer sub-app for `specify self`
- self_check, self_upgrade — `specify self check/upgrade` commands
Dependency rule: _version.py imports only stdlib + packaging + ._console.
Backward compatibility: GITHUB_API_LATEST, self_check, self_upgrade
remain importable from specify_cli via re-exports in __init__.py.
Update test_upgrade.py to import helpers from specify_cli._version and
patch at the correct module path (specify_cli._version.*).
Add test_version_imports.py as regression guard.
* fix(tests): update _fetch_latest_release_tag import path in test_authentication.py
PR-3 moved _fetch_latest_release_tag from specify_cli into
specify_cli._version. test_upgrade.py was updated at the time, but
test_authentication.py::TestFetchLatestReleaseTagDelegation still
imported from the old location, causing ImportError on all three
delegation tests. Update all three inline imports to the correct
module path.
* Add Time Machine extension to community catalog
Add time-machine extension submitted by @teeyo to:
- extensions/catalog.community.json (alphabetical order)
- docs/community/extensions.md community extensions table
Closes#2568
* Fix alphabetical ordering of time-machine entry
Move time-machine before tinyspec in both catalog JSON (by ID)
and docs table (by name), since time < tiny alphabetically.
* docs: fix script name in directory tree examples
Replace update-claude-md.sh with the actual filename setup-tasks.sh
in two directory tree examples (Steps 2 and 4 of the detailed walkthrough).
* docs: fix .specify/scripts layout to show bash/ and powershell/ subdirs
install_shared_infra() installs scripts under .specify/scripts/bash/
(script_type="sh") or .specify/scripts/powershell/ (script_type="ps"),
not directly under .specify/scripts/. Update docs/installation.md and
the _install_shared_infra docstring to reflect the actual on-disk layout.
* docs: update README tree examples to show scripts/bash/ subdirectory
Scripts are installed under .specify/scripts/bash/ (or powershell/)
not directly under .specify/scripts/. Fix both tree diagrams in the
Detailed Process walkthrough to match the actual on-disk layout.
* chore: bump version to 0.8.10
* chore: begin 0.8.11.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Shorten README.md install section to single uv command + link to
installation guide for alternatives and troubleshooting
- Add explicit 'Initialize a project' step to README Get Started
- Remove duplicate Troubleshooting section from README
- Reorder 'Make it your own' card on docs landing page so extensions
and presets are explained before the stats
- Update Community nav-card to link to new community overview
- Create docs/community/overview.md landing page (aligned with
reference/overview.md)
- Create dedicated install sub-pages: pipx, one-time (uvx), air-gapped
- Update docs/installation.md to lead with persistent uv install and
link to sub-pages instead of duplicating content
- Update docs/toc.yml with new pages
- Remove stale EOF file
* Add Agent Governance extension to community catalog
Add agent-governance extension submitted by @bigsmartben to:
- extensions/catalog.community.json (alphabetical order)
- README.md community extensions table
Closes#2552
* Move community extensions table from README to docs site
- Create docs/community/extensions.md with full extensions table
- Replace ~120-line table in README.md with summary + link to docs site
- Add Extensions entry to docs/toc.yml under Community
- Update add-community-extension SKILL.md references
* refactor: extract _assets.py and _utils.py from __init__.py
Move bundle path resolution and version lookup into _assets.py (stdlib only,
zero internal imports), and system utilities (subprocess, tool detection,
file operations) into _utils.py (imports only from ._console). Re-export all
moved symbols from __init__.py for backward compatibility. Update
test_check_tool.py to patch both specify_cli and specify_cli._utils namespaces
since constants are now defined in _utils.
* style: apply PR-1 review patterns to _assets.py and _utils.py
- Add module docstring to _assets.py (stdlib-only, zero internal imports)
- Add blank line after `from __future__ import annotations` in both files
- Replace `Optional[X]` with `X | None` throughout _utils.py (PEP 604)
- Remove unused `Optional` import from _utils.py
- Use explicit re-export form (`X as X`) for public symbols in __init__.py
- Remove unused `subprocess` and `tempfile` imports from __init__.py (moved to _utils.py)
* fix(opencode): use commands/ directory (plural) to match OpenCode docs
OpenCode documentation (https://opencode.ai/docs/commands/) uses
.opencode/commands/ (plural) as the canonical command directory.
The OpenCode runtime supports both .opencode/command/ and
.opencode/commands/ via a {command,commands} glob, but the
singular form was the original convention and is now outdated.
Update the OpenCode integration to write to .opencode/commands/
instead of .opencode/command/, aligning with the documented
standard and the OpenSpec fix (Fission-AI/OpenSpec#748).
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
* feat(registrar): add legacy_dir fallback for backward-compatible directory migration
Add _resolve_agent_dir() to CommandRegistrar that checks a
legacy_dir fallback when the canonical directory does not exist.
When legacy_dir is found, a deprecation warning directs users to
run "specify integration upgrade" to migrate.
The OpenCode integration declares legacy_dir: ".opencode/command"
so that extension and preset registration, as well as command
cleanup, continue working for projects that have not yet migrated
to .opencode/commands/.
The legacy_dir mechanism is opt-in: integrations that do not
declare it get no fallback and no behavioral change.
Add end-to-end test verifying that "specify integration upgrade
opencode" migrates commands from legacy .opencode/command/ to
canonical .opencode/commands/ and removes stale files.
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
* fix(registrar): address PR review feedback on legacy_dir handling
- Fix deprecation warning formatting: quote paths and remove trailing
'/.' that produced confusing '.opencode/commands/.' output
- Eliminate duplicate warnings: pass pre-resolved directory to
register_commands() via _resolved_dir parameter so
_resolve_agent_dir() is only called once per agent
- Fix unregister_commands() to clean both canonical and legacy dirs
when both exist, preventing orphaned command files after upgrade
- Add test_unregister_cleans_legacy_when_both_dirs_exist regression
test and tighten warning count assertion to exactly 1
Assisted-by: OpenCode (claude-opus-4-6)
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
---------
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
* refactor: extract _console.py from __init__.py
Move Rich UI primitives (BANNER, TAGLINE, StepTracker, get_key,
select_with_arrows, console, BannerGroup, show_banner) into a new
src/specify_cli/_console.py module. Re-export all symbols from
__init__.py to preserve the public API. Add regression guard tests.
* refactor(console): improve type annotations and add guard for empty options
- Add module-level docstring documenting the console layer's purpose and
the dependency-layering rule (no imports from other specify_cli modules)
- Tighten select_with_arrows() signature: options typed as dict[str, str]
and default_key as str | None to align with repo typing style
- Add early ValueError guard when options is empty, preventing downstream
ZeroDivisionError / IndexError inside the Live loop
* refactor(console): improve type safety and code quality in _console.py
- Add Callable import from collections.abc for precise callback typing
- Annotate StepTracker._refresh_cb as Callable[[], None] | None
- Add parameter/return types to attach_refresh()
- Use explicit keyword form typer.Exit(code=1) across all error exits
- Add blank line between StepTracker class and get_key() (PEP 8)
- Add regression test for select_with_arrows() raising ValueError on
empty options dict
* style(cli): add __all__ declaration to fix Ruff F401 lint warnings
- Add explicit __all__ for intentional re-exports (BANNER, TAGLINE, get_key)
- Prevent F401 unused import errors in CI lint checks
- Maintain backward compatibility for external imports
* Preserve public console imports
The CLI package intentionally re-exports console helpers for compatibility, so __all__ must track that public surface instead of narrowing star imports to a partial set.
Constraint: Existing tests import console helpers directly from specify_cli
Rejected: Remove __all__ entirely | keeping an explicit export list documents the intended compatibility surface
Confidence: high
Scope-risk: narrow
Directive: Keep __all__ synchronized when adding or removing specify_cli public re-exports
Tested: uv run pytest tests/test_console_imports.py -q
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* style(cli): use explicit re-export syntax to fix ruff F401 warnings
Use `X as X` form for BANNER, TAGLINE, and get_key imports
to mark them as intentional public re-exports and silence
ruff F401 lint errors.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.8.9
* chore: begin 0.8.10.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Rewrite docs/index.md from a philosophy essay into a landing page
organized around four pillars: Spec-driven by default, Use any coding
agent, Make it your own, and Integrate into your organization.
- Add hero section with GitHub Spec Kit branding and CTA buttons
- Add 2x2 pillar card grid with GitHub Primer color accents
- Add community stats section (96K stars, 200+ contributors, etc.)
- Add navigation cards and footer install CTA
- Move SDD philosophy content to docs/concepts/sdd.md
- Add custom DocFX template overlay with card CSS and dark mode
- Set landing layout for index.md via fileMetadata
- Update toc.yml and docfx.json for new concepts section
* Add Spec Scope extension to community catalog
Adds spec-kit-scope: effort estimation and scope tracking from spec artifacts.
4 commands:
- /speckit.scope.estimate — data-driven effort estimation with three-point ranges
- /speckit.scope.compare — side-by-side spec scope comparison
- /speckit.scope.creep — scope creep detection via git history
- /speckit.scope.budget — sprint-ready time budget generation
1 hook: after_specify (auto-estimation)
Turns "how long will this take?" into a data-driven answer.
* Add Spec Changelog extension to community catalog
* Add Spec Changelog extension to community catalog
* fix: drop accidental scope entry, restore Intelligent Agent Orchestrator README row, return Spec Reference Loader to original position
Per Copilot review on PR #2177: this branch is supposed to add only the
Spec Changelog extension. The diff against main also showed (1) a duplicate
'scope' catalog entry, (2) a deletion of the Intelligent Agent Orchestrator
README row, and (3) Spec Reference Loader moved out of alphabetical order.
All three were merge artifacts and have been cleaned up here.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: keep Spec Changelog row alphabetically sorted
Address Copilot review on PR #2177: the Community Extensions table is
sorted alphabetically by display name, and 'Changelog' precedes 'Critique',
'Diagram', 'Orchestrator', and 'Reference', so the Spec Changelog row
belongs right after Ship Release Extension. Move it into its sorted slot
and keep Spec Reference Loader in its original alphabetical position
(between Spec Orchestrator and Spec Refine).
* fix: remove duplicate Spec Reference Loader row from README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(catalog): add BrownKit (brownkit) community extension (#2510)
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: bump catalog-level updated_at to match newest entry
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(kiro-cli): replace literal $ARGUMENTS with prose fallback
Kiro CLI file-based prompts do not natively substitute any
argument placeholder (kirodotdev/Kiro#4141, kiro.dev/docs/cli
manage-prompts), so the literal "$ARGUMENTS" set in
KiroCliIntegration.registrar_config["args"] reached the model
verbatim and broke the prompt — every parameterized SpecKit
command under Kiro CLI was unusable.
Replace the placeholder with a prose fallback that instructs
the model to take its argument from the user's next message,
mirroring the convention used by other integrations whose
target CLI lacks native argument injection.
Add two regression tests in TestKiroCliIntegration:
- test_rendered_prompts_do_not_contain_raw_arguments
- test_rendered_prompts_contain_kiro_arg_placeholder
and override the inherited test_registrar_config so it does
not require args == "$ARGUMENTS".
Fixes#1926
* test(kiro-cli): tighten args regression guard + document quirk
Address review feedback on PR #2482.
Two changes that bracket the original bug fix from both sides — code AND
documentation:
1. Test layer (Copilot finding at lines 27, 56)
The previous test_registrar_config asserted only that args != "$ARGUMENTS"
and that args is truthy. That would silently pass if a future change
swapped $ARGUMENTS for $INPUT, {{userMessage}}, <args>, or any other
unsubstituted placeholder syntax — defeating the regression guard for
issue #1926.
Replace with a dual-layer guard:
- test_registrar_config_args_is_exact_prose_fallback pins args to the
imported _KIRO_ARG_FALLBACK constant. Wording drift now requires a
deliberate paired commit (production constant + test).
- test_registrar_config_args_does_not_look_like_a_placeholder_token is
an independent regression guard built on a 7-pattern regex set
covering Bash ($X, ${X}, ${X:-default}), Mustache/Handlebars/Jinja
({{X}}, {{{X}}}), Liquid/Jinja control ({% %}), Python str.format /
.NET ({0}, {var}), angle-bracket (<X>), and Windows (%X%). Patterns
are anchored to the full string so legitimate prose mentioning a
placeholder ("the {{magic}} of placeholders") is not flagged.
Also fix the line-56 tautology by importing _KIRO_ARG_FALLBACK directly
into test_rendered_prompts_contain_kiro_arg_placeholder, instead of
reading the constant back from registrar_config["args"]. The test now
verifies the FALLBACK STRING reaches the rendered output, independent
of the integration's own config staying correct.
2. Docs layer (mnriem CHANGES_REQUESTED)
The Kiro CLI row in docs/reference/integrations.md only documented its
alias. Update the notes column to lead with the limitation — Kiro CLI
does not substitute $ARGUMENTS in file-based prompts, so Spec Kit ships
a prose fallback at render time — with inline links to upstream Kiro
"Manage prompts" docs and issue #1926. Style follows the Pi row
("limitation first, alias preserved at end").
Refs #1926
* Add game-narrative-writing preset to community catalog
- Preset ID: game-narrative-writing
- Version: 1.0.0
- Author: Andreas Daumann
- Description: Spec-Driven Development for interactive game narrative for pre-production for video games. Authors write in a portable generic format, Twine/Sugarcube (.twee) or Ink (.ink). Covers choice-IF, visual novels, and branching dialogue. Supports Tier 1 mechanic hooks (flag, counter, inventory, timer, trust, currency, npc_state, ending_condition), multi-ending design, series carry-over variable registry, and NPC-focused character architecture.
Co-authored-by: Copilot <copilot@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>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* test(presets): silence expected UserWarnings in self-test composition tests
The self-test preset that ships with the repo provides a wrap-strategy
command (speckit.wrap-test) intentionally without a corresponding core
base layer, exercising the 'no base layer' branch of
_reconcile_composed_commands().
Eighteen tests across TestSelfTestPreset and TestPresetSkills install
this preset and trigger an expected UserWarning. Running the suite with
-W error::UserWarning surfaces them as test noise that could obscure
unrelated warnings.
Add class-level pytest.mark.filterwarnings filters to acknowledge the
two known messages ('Cannot compose command speckit.wrap-test' and
'Post-install reconciliation failed for self-test') so other UserWarning
sources still propagate normally.
Fixes#2363
* test(presets): scope filterwarnings to UserWarning category
Address Copilot review on #2373: the previous filterwarnings entries
omitted the warning category, so any warning class with a matching
message would have been silenced. Append :UserWarning to the four
filters so only the deliberately-emitted UserWarnings from
_reconcile_composed_commands() are ignored.
* test(presets): narrow self-test warning filter to install helper only
Address Copilot feedback: the class-level @pytest.mark.filterwarnings on
TestPresetSkills was too broad. The 'Post-install reconciliation failed'
filter could mask real reconciliation regressions, since that warning is
only emitted when _reconcile_composed_commands/_reconcile_skills raises.
Tests in TestPresetSkills already call install_self_test_preset(), which
scopes a narrow filter to the expected wrap-strategy 'Cannot compose'
warning. The class-level filters are redundant for those calls and unsafe
elsewhere, so they are removed.
* test(presets): align TestSelfTestPreset docstring with helper-based filtering
Address Copilot feedback: docstring referred to 'filters above', but the
fix uses warnings.filterwarnings inside install_self_test_preset rather
than class-level decorators. Updated the docstring to describe the actual
mechanism.
* test(presets): remove extra blank line between helper and class (PEP 8)
Address Copilot feedback: PEP 8 expects two blank lines between top-level
definitions; reduce the three blank lines between install_self_test_preset
and TestSelfTestPreset to two.
* chore: bump version to 0.8.8
* chore: begin 0.8.9.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(catalog): add Spec Kit Schedule (schedule) community extension
CP-SAT scheduler for spec-kit projects with multi-agent task
optimization. Adds catalog entry for v0.5.2 release.
Pre-flight verification:
- archive/refs/tags/v0.5.2.zip resolves (HTTP 200, 718322 bytes,
SHA-256 00d4dab1df680e5888e0d0e861eb4696ace00661d40669bf719a75dc379b40b5)
- extension.yml schema_version 1.0, id 'schedule', 3 commands
(speckit.schedule.run, speckit.schedule.portfolio, speckit.schedule.visualize)
- 566 tests passing on Ubuntu 3.10/3.11/3.12 + macOS 3.12 (all blocking)
- 92.51% line coverage, mypy --strict on 28 modules
- Sigstore attestations via attest-build-provenance@v2 (gh attestation
verify exit 0 confirmed)
- 4 worked examples + replan demo runnable via bash bin/run-examples.sh
License: MIT
speckit_version: >=0.4.0
* fix(catalog): update Spec Kit Schedule entry to v0.5.3
v0.5.2 had two real-world install bugs caught when a user tried the
documented commands:
1. README/INSTALL showed 'specify extension add --from URL' (missing
the EXTENSION positional arg). The canonical form is
'specify extension add schedule --from URL'. Fixed in v0.5.3.
2. Release zip was ~5x bigger than peer extensions due to dev cruft
(.github/, tests/, benchmarks/, build metadata). Added .gitattributes
export-ignore in v0.5.3, dropping the zip from 718 KB to 590 KB.
v0.5.3 archive verified HTTP 200, sigstore attestations active.
* fix(catalog): bump Spec Kit Schedule entry to v0.5.4
Adds an opt-in after_tasks hook so users get prompted to run the
scheduler immediately after /speckit.tasks, without forcing it.
Mirrors the canonical pattern used by the bundled 'git' extension.
* fix(catalog): bump Spec Kit Schedule entry to v0.5.5
Documents the after_tasks hook in README and rewrites the
/speckit.schedule.portfolio command to autodetect the project's
tech stack via solver.autodetect, then refine interactively
against the matching recipe in docs/portfolio-design.md, instead
of starting from a blank slate.
* fix(catalog): bump Spec Kit Schedule entry to v0.6.0
State now encapsulated under .specify/, /speckit.schedule.run is
idempotent with auto-bootstrap, and portfolio detection is
AI-aware (reads .specify/integration.json and discovers the user's
fleet from the canonical location for whichever spec-kit AI
assistant they chose: claude, copilot, cursor-agent, gemini, or any
of the other 26 supported integrations).
* fix(catalog): bump Spec Kit Schedule entry to v0.6.1
Per-AI portfolio templates with verified May 2026 GA models
(gpt-5.5 flagship, claude-opus-4-7, gemini-2.5-flash). Critical
price unit fix (cost_aware reported $ figures 1000× inflated
in v0.6.0). Plus calibration feedback loop and inline summary.
* fix(readme): add Spec Kit Schedule row to Community Extensions table
Per Copilot review on PR #2473: the publishing guide requires an
accepted submission to update both extensions/catalog.community.json
AND the root README's Community Extensions table. Without the README
row the extension wouldn't appear in the primary browsable list.
Inserted alphabetically between 'Spec Diagram' and 'Spec Orchestrator'.
Category: process. Effect: Read+Write.
* fix(catalog): provides.commands 3→4 (schedule only) + bump top-level updated_at
Surgical edit responding to two Copilot review nits on PR #2473.
Previous attempt used str.replace too broadly and was reverted —
this version uses unique anchors to mutate only the schedule
entry and the top-level updated_at field.
1. extensions/catalog.community.json schedule entry had
provides.commands: 3, but the extension exposes 4 commands
(run, portfolio, visualize, calibrate — calibrate was added
in v0.6.0 Build 2 / calibration feedback loop).
2. Top-level catalog updated_at was 2026-05-06T22:28:55Z but
per-entry updated_at for our schedule entry is 2026-05-07.
Since this PR modifies the catalog, the top-level timestamp
advances too.
* fix(catalog): bump Spec Kit Schedule entry to v0.6.2
Adds /speckit.schedule.status (5th command) — self-diagnose
installation state, distinguishes 'expected-missing' (will
bootstrap automatically) from 'missing' (real problem). Closes
the audit-tool false-alarm gap where schedule-config.yml absence
post-install was misread as broken state.
---------
Co-authored-by: Julio César Franco Ardila <noreply@anthropic.com>
* fix(integration): refresh shared infra on integration switch
* fix(integration): address Copilot review on switch shared-infra refresh
- Clarify install_shared_infra docstring: force overwrites regular files
but always preserves symlinks (safe-destination check refuses to follow).
- Print refresh_hint only for preserved_user_files; skipped_files keeps
the generic remediation. Avoids misleading guidance when files were
merely skipped (not detected as customized).
- Catch ValueError from the safe-destination check and bucket the path
under a new symlinked_files warning instead of aborting the switch.
- Restore templates/constitution-template.md to upstream (drop accidental
leading blank lines).
* fix(integration): narrow symlink bucketing to dedicated exception
Address Copilot feedback on shared_infra.py:305 — _safe_dest_or_bucket
caught any ValueError as 'symlinked', which masked genuine safety errors
(path escape, parent-not-a-directory).
- Introduce SymlinkedSharedPathError(ValueError) raised only by the
symlink-specific branches in _ensure_safe_shared_*().
- _safe_dest_or_bucket() now catches only SymlinkedSharedPathError;
other ValueErrors propagate so the operation aborts with the real
cause instead of being silently bucketed.
- Wrap top-level dest_scripts/dest_variant/dest_templates mkdir calls
in the same bucket helper so a symlinked .specify/scripts or
.specify/templates is preserved with a warning rather than aborting
the switch (matches the documented 'preserve customizations' behavior).
- Update tests to expect the new bucket+warn behavior for leaf-level
symlinked destinations.
* fix(integration): tailor shared-infra warnings and rename preflight test
Address Copilot review on PR #2375:
- skipped_files hint now uses refresh_hint when refresh_managed=True
so integration switch suggests --refresh-shared-infra instead of the
generic init/upgrade flags.
- symlinked-files warning header says "path(s)" rather than "file(s)"
since symlinked directories (e.g. .specify/scripts/bash) are also
bucketed there.
- Rename test_shared_infra_install_preflights_before_writing to
test_shared_infra_install_buckets_unsafe_destinations_and_continues
to match the new bucket-and-continue semantics.
* test: rename symlink bucketing tests to reflect bucket-and-continue behavior
The two file-bucketing tests at line 300/320 were named *_refuses_*, but
the new behavior buckets symlinked file destinations with a warning while
safe destinations in the same install still complete. Rename to
*_buckets_* and update docstrings to match.
The remaining *_refuses_* tests (line 342/362/381) genuinely raise on
symlinked dirs/manifests and keep their names.
---------
Co-authored-by: Quratulain-bilal <quratulain.bilal@users.noreply.github.com>
* chore: bump version to 0.8.7
* chore: begin 0.8.8.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: update extension versions in community catalog
- Update architecture-guard from v1.4.0 to v1.6.7
- Update memory-md from v0.7.5 to v0.7.9
- Update security-review from v1.4.2 to v1.4.5
All extensions now point to latest release downloads.
* chore: update timestamps in community catalog
Co-authored-by: Copilot <copilot@github.com>
---------
Co-authored-by: Copilot <copilot@github.com>
* add lingma support
* fix
* fix context file
* Update CONTEXT_FILE path in test integration
* fix IntegrationOption.default
* fix IntegrationOption.defaultfix
* fix: address Copilot review feedback
- Add blank line after __future__ import (PEP 8)
- Remove trailing whitespace at end of lingma/__init__.py
- Bump integrations/catalog.json updated_at timestamp
- Add Lingma to supported agent list in README.md
* fix: address Copilot review feedback (round 4)
- Reword module docstring: Lingma is a brand-new skills-only integration
with no prior command-mode history, so 'deprecated since v0.5.1'
wording (copied from Trae) was misleading
- Remove Lingma from README CLI-tool check list: Lingma is IDE-based
(requires_cli=False) and is explicitly skipped by specify init /
specify check tool detection
* fix(forge): use hyphen notation for command refs in Forge integration
- Add invoke_separator = "-" class attribute to ForgeIntegration so
effective_invoke_separator() returns "-" for shared-template installs
- Add "invoke_separator": "-" to ForgeIntegration.registrar_config so
agents.py CommandRegistrar can resolve refs with the correct separator
- Pass invoke_separator to process_template() in ForgeIntegration.setup()
so all .forge/commands/*.md bodies use /speckit-foo notation
- Replace literal /speckit.specify with __SPECKIT_COMMAND_SPECIFY__ in
extensions/git/commands/speckit.git.feature.md so every agent resolves
the reference through its own separator
- Apply resolve_command_refs re.sub in agents.py register_commands() after
argument-placeholder substitution so extension commands registered for
Forge get /speckit-foo refs; all other agents continue to get /speckit.foo
Fixes ZSH compatibility: dot-notation command invocations (/speckit.specify)
are misinterpreted by ZSH as file-path operations; hyphen notation
(/speckit-specify) works correctly in all shells.
* fix(agents): propagate invoke_separator from integration class into AGENT_CONFIGS
Skills-based agents (claude, codex, kimi, …) inherit invoke_separator="-"
from SkillsIntegration but do not repeat it in their registrar_config dicts.
_build_agent_configs() was copying registrar_config verbatim, so
register_commands() fell back to "." when resolving __SPECKIT_COMMAND_*__
tokens for those agents — emitting /speckit.specify instead of the correct
/speckit-specify for extension commands like speckit.git.feature.
Fix: after copying registrar_config, inject invoke_separator from the
integration's class attribute when it is not already declared explicitly.
This makes the integration class the single source of truth for all agents,
without requiring each SkillsIntegration subclass to duplicate the field.
Also replace the inline re.sub in register_commands() with a call to
IntegrationBase.resolve_command_refs() (deferred import to avoid the
existing circular dependency) so token-resolution logic is not duplicated.
Adds two tests in test_agent_config_consistency.py:
- test_skills_agents_have_hyphen_invoke_separator_in_agent_configs: asserts
every /SKILL.md agent has invoke_separator="-" in AGENT_CONFIGS.
- test_skills_agent_command_token_resolves_with_hyphen: end-to-end check via
CommandRegistrar that the git extension's speckit.git.feature command is
installed for Claude with /speckit-specify (not /speckit.specify).
Addresses review comment on PR #2462.
* feat(catalog): add Cost Tracker (cost) community extension
Adds a new entry for spec-kit-cost — track real LLM dollar cost across
SDD workflows with per-feature budgets, per-integration comparison,
and finance-ready exports.
Repo: https://github.com/Quratulain-bilal/spec-kit-cost
Release: v1.0.0
* docs(catalog): add Cost Tracker README row, bump updated_at
Address Copilot review feedback:
- Add Cost Tracker row to README community extensions table
- Bump top-level updated_at per EXTENSION-PUBLISHING-GUIDE.md
* fix(catalog): address Copilot feedback on cost extension entry
- Move cost entry after confluence so the c* block is alphabetized
- Bump top-level updated_at to 2026-05-05 per EXTENSION-PUBLISHING-GUIDE
- Use documented 'visibility' category in README (not 'analytics'),
matching Token Consumption Analyzer's classification
- Replace 'analytics' tag with 'visibility' in catalog tags for consistency
* fix(catalog): bump top-level updated_at for cost entry addition
Address Copilot feedback: the file-level updated_at must be bumped on
every catalog change per EXTENSION-PUBLISHING-GUIDE.md:204-205.
---------
Co-authored-by: Quratulain-bilal <quratulain.bilal@users.noreply.github.com>
* chore: bump version to 0.8.6
* chore: begin 0.8.7.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Simplify the community catalog submission flow to use issue templates
with manual maintainer review (no automation scripts or workflows).
- Add explicit CODEOWNERS entries for catalog.community.json files so
submissions are automatically assigned to a maintainer for review
- Improve preset submission template:
- Add 'Required Extensions' optional field
- Make 'Templates Provided' optional (supports command-only presets)
- Add 'Number of Scripts' optional field
The existing extension and preset issue templates already collect all
required catalog metadata. Maintainers review submissions and manually
update the catalog JSON files.
Closes#2400
* fix: validate URL scheme in build_github_request
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* test: add missing hostname validation test for build_github_request
* fix: update docstring and fix import grouping per Copilot feedback
* fix: sort imports and simplify url validation in build_github_request
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add Architecture Guard to community catalog
- Add architecture-guard v1.4.0 extension entry to catalog
- Add entry to README community extensions table
- Includes built-in Laravel-specific governance rules
* chore: update catalog timestamp to 2026-05-05
* fix: address PR feedback
- Add 'governance' category to README legend (used by Architecture Guard)
- Update architecture-guard timestamps to 2026-05-05 (submission date)
- Align with published extension behavior (Laravel support now built-in)
* chore: update Architecture Guard category to process
- Changed from 'governance' to 'process' (official category)
- Aligns with schema in EXTENSION-PUBLISHING-GUIDE.md
- Removed 'governance' from category legend (not an official category)
* chore: update timestamps to actual UTC datetime
- Top-level updated_at: 2026-05-05T07:26:00Z
- Entry created_at/updated_at: 2026-05-05T07:26:00Z
- Replaces placeholder 00:00:00Z with actual submission time
* chore: bump version to 0.8.5
* chore: begin 0.8.6.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(presets): add Spec2Cloud preset for Azure deployment workflow
Co-authored-by: Copilot <copilot@github.com>
* feat(presets): add Spec2Cloud preset details to community catalog
* fix(presets): update Spec2Cloud URL to point to the correct GitHub repository
* feat(presets): update Spec2Cloud entry with created_at and updated_at timestamps
* feat(presets): update Spec2Cloud version to 1.1.0 and adjust timestamps
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: update spec2cloud preset details and resolve merge conflicts
* fix: reorder Spec2Cloud entry in community presets for consistency
---------
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.8.4
* chore: begin 0.8.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: migrate extension commands on integration switch
When switching integrations (e.g. kimi → opencode), extension commands
were not re-registered for the new agent, leaving the new agent without
extension support and orphaning files in the old agent's directory.
Changes:
- Add ExtensionManager.unregister_agent_artifacts() to clean up old
agent extension files and registry entries during switch
- Add ExtensionManager.register_enabled_extensions_for_agent() to
re-register all enabled extensions for the new agent
- Wire both into integration_switch() after uninstall/install phases
- Handle skills mode (Copilot --skills) correctly
- Add tests for kimi→opencode→claude migration, Copilot skills mode,
and disabled extension handling
Fixes extension commands not appearing after integration switch.
* Update src/specify_cli/extensions.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.8.3
* chore: begin 0.8.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add Work IQ extension to community catalog
Adds the Work IQ extension by sakitA to the community catalog.
Work IQ integrates Microsoft 365 organizational knowledge (emails,
meetings, documents, Teams) into spec-driven development workflows.
- 4 commands: ask, context, stakeholders, enrich
- 2 hooks: before_specify, after_specify
- Requires: speckit >=0.1.0, Node.js >=18.0.0, workiq CLI
Repository: https://github.com/sakitA/spec-kit-workiq
* Address PR review comments
- Fix download_url to use .zip (Spec Kit installer requires ZIP format)
- Bump top-level catalog updated_at to 2026-04-29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Sakit Atakishiyev <satakishiyev@microsoft.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(integrations): add Devin for Terminal skills-based integration
- Register DevinIntegration as a SkillsIntegration with .devin/skills/ layout
- Add catalog entry, docs row, and supported-agents listing
- Display /speckit-<command> hyphen syntax in init "Next Steps" panel
(matches Claude/Cursor/Copilot skills mode, since Devin invokes skills
by directory name)
Closes#2346
* fix(devin): implement -p non-interactive dispatch; clarify skills comment
Addresses Copilot review on PR #2364:
- Override build_exec_args() in DevinIntegration to emit
'devin -p <prompt> [--model X]' for non-interactive text dispatch
(verified Devin CLI supports -p / --print). Returns None when
output_json=True since Devin has no structured-output flag, so
CommandStep workflows that require JSON cleanly raise
NotImplementedError instead of crashing on an unknown CLI flag.
requires_cli=True is retained for tool detection.
- Extend the skills-integrations enumeration comment in
specify_cli/__init__.py to include copilot and devin so the
comment matches the code below it.
* fix(devin): always return exec args; document plain-text stdout
Addresses third Copilot review comment on PR #2364.
Returning None from build_exec_args() when output_json=True
incorrectly used the codebase's IDE-only sentinel: workflow
CommandStep checks 'impl.build_exec_args("test") is None' to
detect non-dispatchable integrations (test_workflows.py exercises
this with WindsurfIntegration). The previous implementation made
Devin appear non-dispatchable to all command steps even though it
runs fine via 'devin -p'.
Always return the args list. When output_json is requested, Devin
is still dispatched and returns plain-text stdout instead of
structured JSON; the docstring documents this explicitly.
* docs(devin): include claude in skills-integrations enumeration comment
Addresses Copilot review on PR #2364: the comment listing skills
integrations omitted Claude, which is also a SkillsIntegration
subclass. Updated to keep the comment accurate for future readers.
* test(devin): add build_exec_args regression tests; bump catalog updated_at
Addresses Copilot review on PR #2364, per @mnriem's request to
'address the Copilot feedback, especially the testing ask':
- tests/integrations/test_integration_devin.py: add TestDevinBuildExecArgs
with three regression assertions:
* build_exec_args returns args (not the None IDE-only sentinel)
* --output-format is never emitted, regardless of output_json
* --model flag is passed through correctly
- integrations/catalog.json: bump top-level updated_at to reflect the
Devin entry addition so downstream catalog consumers can detect the
change reliably.
The compatibility-error messages in extensions.py and presets.py, plus the
extension troubleshooting guide, told users to upgrade with:
uv tool install specify-cli --force
Without `--from git+https://github.com/github/spec-kit.git`, uv resolves
`specify-cli` from PyPI, where an unrelated package with the same name
(no author, no project URLs) ships a stub CLI that lacks `extension`,
`preset`, and most spec-kit commands. Users following the upgrade hint
land on the squat package and report "extension command removed"
(see #1982).
Reuse the existing `REINSTALL_COMMAND` constant in extensions.py and
import it from presets.py so all three call sites point at the GitHub
source. The doc fix also adds a one-line note explaining the PyPI
collision so the same advice doesn't get re-stripped later.
Refs #1982
Update v-model extension entry in community catalog to reflect the v0.6.0
release: https://github.com/leocamello/spec-kit-v-model/releases/tag/v0.6.0
Highlights of v0.6.0:
- Domain Overlay Architecture (9 overlay manifests; automotive, medical,
aerospace, general)
- ID Lifecycle Model (Proposed -> Active -> Deprecated -> Removed)
- Standards enrichment across all 11 commands (IEEE 1012:2016, ISO
25010:2023, ISO 42030:2019, ISO 12207:2017, IEEE 1016, IEEE 29148,
ISO 29119-4, ISO 14971, DO-178C, ARP4761A, INCOSE SE Handbook)
- Aerospace DO-178C support: Flight Warning Computer DAL-A golden fixture
- Test infrastructure: fixtures reorganized; +11 LLM-as-judge evals (42 -> 53)
Command count remains 14 (no new commands added in this release).
Stars updated to live count (21) from GitHub API.
* feat: add threatmodel extension to community catalog
* update timestamp for catalogue freshness
* update timestamp for catalogue freshness
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update README.md
update readme.md with spec-kit-threatmodel
---------
Co-authored-by: Samal <navia.samal@sap.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update preset-fiction-book-writing to community catalog
- Preset ID: fiction-book-writing
- Version: 1.5.0
- Author: Andreas Daumann
- Description: Spec-Driven Development for novel and long-form fiction. Replaces software engineering terminology with storytelling craft: specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports 8 POV modes, all major plot structure frameworks, 5 humanized-AI prose profiles, and exports to DOCX/EPUB/LaTeX via pandoc. V1.5.0: Support interactive, audiobooks, series, workflow corrections
* Add fiction-book-writing preset to community catalog
- Preset ID: fiction-book-writing
- Version: 1.6.0
- Author: Andreas Daumann
- Description: Added support for 12 languages, export with templates, cover builder, bio builder, workflow fixes
* Update presets/catalog.community.json
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fixed update_at for fiction-book-writing preset
* Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fixed description for fiction-book-writing
* "Add fiction-book-preset to community catalog
- Preset ID: fiction-book-writing
- Version: 1.7.0
- Author: Andreas Daumann
- Description: It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose. Supports interactive elements like brainstorming, interview, roleplay and extras like statistics, cover builder and bio command. Export with templates for KDP, D2D etc. V1.7.0: Support for offline semantic search.
* Update presets/catalog.community.json
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update presets/catalog.community.json
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add fiction-book-writing to community catalog
- Preset ID: fiction-book-writing
- Version: 1.7.0
- Author: Andreas Daumann
- Description: Spec-Driven Development for novel and long-form fiction. RAG support
* Update docs/community/presets.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix(extensions): use explicit UTF-8 encoding when reading manifest YAML
On Windows, Python's open() defaults to the system locale encoding
(e.g., GBK on Chinese Windows), which causes UnicodeDecodeError when
extension.yml or preset.yml contains non-ASCII content such as Chinese
characters in description fields.
Add encoding='utf-8' to ExtensionManifest._load_yaml and
PresetManifest._load_yaml so manifests are read consistently across
platforms.
Fixes#2325
* test(extensions,presets): add UTF-8 manifest regression tests for #2325
Positive: extension.yml/preset.yml with non-ASCII (Chinese + emoji)
descriptions load correctly when written as UTF-8 bytes — fails on
Windows without explicit encoding='utf-8'.
Negative: files containing invalid UTF-8 bytes raise a clean error
(ValidationError or UnicodeDecodeError), not a silent crash.
* fix(extensions,presets): wrap I/O and decode errors as ValidationError
Address remaining Copilot concerns on #2370:
- Catch UnicodeDecodeError and OSError in both manifest loaders and
re-raise as ValidationError / PresetValidationError so callers see a
consistent error type, not a bare decode/IO traceback.
- Validate that PresetManifest YAML root is a mapping (extensions.py
already had this; presets.py was missing it). Treat None as {} for
empty-file compatibility.
- Tighten the negative regression tests to assert the specific message,
and add a non-mapping-root test for PresetManifest matching the
existing one for ExtensionManifest.
Add Microsoft 365 Integration to community catalog and README.
Ingests Teams messages, files, and meeting transcripts as Markdown
for use with speckit specify.
* docs: replace deprecated --ai flag with --integration in all documentation
Replace all user-facing --ai, --ai-skills, and --ai-commands-dir references
with their modern equivalents:
- --ai <agent> → --integration <agent>
- --ai-skills → --integration-options="--skills"
- --ai-commands-dir <dir> → --integration generic --integration-options="--commands-dir <dir>"
Updated files:
- README.md (~17 occurrences)
- docs/installation.md (~8 occurrences)
- docs/upgrade.md (~11 occurrences)
- docs/local-development.md (~5 occurrences)
- CONTRIBUTING.md (1 occurrence)
- extensions/EXTENSION-USER-GUIDE.md (1 occurrence)
- src/specify_cli/__init__.py (docstring examples and error messages)
Left unchanged:
- CHANGELOG.md (historical record)
- Test files (intentionally exercise deprecated flag path)
- CLI flag implementation (backward compatibility)
Closes#2358
* docs: address review feedback on pre-existing issues
- Fix duplicate copilot example in README.md (replace with codex)
- Fix invalid gemini --integration-options="--skills" example (gemini
does not support --skills)
- Update generic integration comment from 'Unsupported agent' to
'Bring your own agent; requires --commands-dir'
- Clarify EXTENSION-USER-GUIDE.md: skills auto-register for
skills-based integrations, not only with --integration-options
* docs: replace bare 'AI agent' / 'AI assistant' with 'coding agent' throughout
Full sweep across all documentation and user-facing CLI messages to
align terminology. Bare references like 'AI agent', 'AI assistant',
and 'AI Agent' are replaced with 'coding agent' or 'coding agent
integration' as appropriate.
Intentionally left unchanged:
- 'AI coding agent' (already correct expanded form)
- Deprecated --ai flag help text and error messages (describes the
deprecated flag itself)
- Community extension descriptions (external project text)
- 'generated by an AI' in CONTRIBUTING.md (general AI, not agent)
* docs: address review — remove deprecated --offline, qualify --skills scope
- Remove --offline from docstring examples (deprecated no-op)
- Remove --offline from CONTRIBUTING.md testing example
- Replace --offline instructions in docs/installation.md with note that
bundled assets are used by default
- Qualify --integration-options="--skills" in README.md to note it only
applies to integrations that support skills mode
* feat(extensions,presets): authenticate GitHub-hosted catalog and download requests with GITHUB_TOKEN/GH_TOKEN
Squashed from #2087 (original author: @anasseth).
Adds GitHub-token authentication to extension and preset catalog fetching
and ZIP downloads so private GitHub repos work when GITHUB_TOKEN/GH_TOKEN
is set, while preventing credential leakage to non-GitHub hosts.
- Introduces shared _github_http module with build_github_request() and
open_github_url() helpers
- Routes ExtensionCatalog and PresetCatalog network calls through
GitHub-auth-aware opener
- Adds comprehensive unit/integration tests for auth header behavior
- Updates user docs for both extensions and presets
Co-authored-by: anasseth <16745089+anasseth@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(auth): address review feedback from #2087
- Fix redirect handler to preserve Authorization on GitHub-to-GitHub
redirects (e.g. github.com → codeload.github.com). The previous
implementation relied on super().redirect_request() which strips
auth on cross-host redirects, breaking private repo archive downloads.
- Add codeload.github.com to documented host lists in both
EXTENSION-USER-GUIDE.md and presets/README.md
- Add redirect auth-preservation and auth-stripping tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(auth): use Bearer scheme instead of token for consistency
Aligns with the rest of the codebase (e.g. __init__.py:1721) and
GitHub's current API guidance. Updates all test assertions accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address second round of Copilot review feedback
- Fix docstring to say Bearer instead of token (matches implementation)
- Remove unused imports/fixtures from redirect tests (GITHUB_HOSTS,
MagicMock, temp_dir, monkeypatch)
- Replace __import__('io').BytesIO() with normal import io pattern
in test_presets.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: anasseth <16745089+anasseth@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(init): deprecate --no-git flag, gate deprecations at v0.10.0
- Add deprecation warning when --no-git is used on specify init
- Update --ai deprecation gate from 1.0.0 to 0.10.0
- Update test expectation for the new version gate
Closes#2167
* fix: address PR review feedback
- Update --no-git deprecation message to reference existing 'specify extension'
commands instead of non-existent --extension flag
- Add test_no_git_emits_deprecation_warning CLI test
* fix: strengthen --no-git deprecation test assertions
Add assertions unique to the --no-git message ('will be removed',
'git extension will no longer be enabled by default') to prevent
false positives from the --ai deprecation panel.
* chore: bump version to 0.8.1
* chore: begin 0.8.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(vibe): migrate to SkillsIntegration and inject user-invocable frontmatter
Switches VibeIntegration from the old prompts-based MarkdownIntegration to SkillsIntegration, adopting the .vibe/skills/speckit-<name>/SKILL.md layout required by Mistral Vibe v2.0.0+. Post-processes each generated SKILL.md to inject `user-invocable: true` so skills are directly callable by users, not just by other agents.
* test(vibe): assert user- invocable: true is present in all generated SKILL.md files
* Update tests/integrations/test_integration_vibe.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* docs: move community presets table to docs site, add missing entries
- Move the full community presets table from README.md to the docs site
at docs/community/presets.md, replacing the README section with a
short link (matching the pattern used for Walkthroughs and Friends).
- Add missing Jira Issue Tracking and Screenwriting rows to the docs
table so it reflects all entries in catalog.community.json.
* docs(presets): add docs site table step to publishing guide
Add step to update docs/community/presets.md when submitting a
community preset, and add corresponding PR checklist item. Matches
the pattern used in the extensions publishing guide.
* Clarify alphabetical sort key in presets publishing guide
Specify that the docs table should be sorted by preset name (the first
column), disambiguating from the catalog JSON which sorts by preset ID.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Address review: fix provides count, admonition style, example row
- Add missing scripts count to Fiction Book Writing table row to match catalog
- Switch README disclaimer to GitHub admonition format for consistency
- Include optional scripts count in PUBLISHING.md example row
* Fix Fiction Book Writing link text to match actual repo name
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* docs(presets): add lean preset README and enrich catalog metadata
- Add README.md documenting the lean workflow preset, its commands,
when to use it, and development instructions.
- Add license, requires.speckit_version, and provides.commands fields
to the lean preset catalog entry.
- Add "core" tag to preset.yml for discoverability.
* fix: bump catalog updated_at and add provides.templates for consistency
Address PR review feedback:
- Bump updated_at to reflect catalog modification time
- Add provides.templates (0) to lean preset entry for consistency
with catalog schema used in catalog.community.json
* fix: resolve command references per integration type (dot vs hyphen)
Replace hardcoded /speckit.<cmd> references in templates with
__SPECKIT_COMMAND_<NAME>__ placeholders that are resolved at
setup time based on the integration type:
- Markdown/TOML/YAML agents: separator='.' → /speckit.plan
- Skills agents: separator='-' → /speckit-plan
Changes:
- Add resolve_command_refs() static method to IntegrationBase
- Add invoke_separator class attribute (. for base, - for skills)
- Wire into process_template() as step 8
- Update _install_shared_infra() to process page templates
- Replace /speckit.* in 5 command templates and 3 page templates
- Add unit tests for resolve_command_refs (positive + negative)
- Add integration tests verifying on-disk content for all agents
- Add end-to-end CLI tests for Claude (skills) and Copilot (markdown)
Fixes#2347
* review: use effective_invoke_separator() for Copilot skills mode
Address PR review feedback: instead of bleeding _skills_mode
knowledge into the CLI layer, add effective_invoke_separator()
method to IntegrationBase that accepts parsed_options.
CopilotIntegration overrides it to return "-" when skills
mode is requested. The CLI layer simply asks the integration
for its separator — no hasattr or _skills_mode coupling.
Also adds tests for the new method on both base and Copilot,
plus an end-to-end test for 'specify init --integration copilot
--integration-options --skills' verifying page templates get
hyphen refs.
* fix: build_command_invocation preserves full suffix for extension commands
Previously rsplit('.', 1)[-1] on 'speckit.git.commit' yielded
just 'commit', producing /speckit.commit instead of
/speckit.git.commit (or /speckit-git-commit for skills).
Fix: strip only the 'speckit.' prefix when present, then join
remaining segments with the appropriate separator.
Updated in IntegrationBase, SkillsIntegration, and
CopilotIntegration. Added tests for extension commands in
build_command_invocation across all three.
* fix: Copilot dispatch_command() preserves full extension command suffix
dispatch_command() had the same rsplit('.', 1)[-1] bug as
build_command_invocation() — speckit.git.commit would dispatch
as /speckit-commit instead of /speckit-git-commit in skills
mode, or --agent speckit.commit instead of speckit.git.commit
in default mode.
* Update product-forge to v1.5.0 in community catalog
- Extension ID: product-forge
- Version: 1.1.1 → 1.5.0
- Author: VaiYav
- Description: updated to reflect v1.5 features (portfolio, lite mode,
monorepo, optional V-Model)
- Commands: 10 → 29
- Tags: refreshed to reflect current surface area
- download_url pinned to v1.5.0 release tag
- updated_at bumped to 2026-04-24
Release: https://github.com/VaiYav/speckit-product-forge/releases/tag/v1.5.0
* Bump product-forge to v1.5.1 (docs patch)
Follow-up to v1.5.0 that surfaces the optional V-Model dependency
(leocamello/spec-kit-v-model ≥ 0.5.0) in README Requirements,
config-template.yml, and docs/config.md. Docs-only patch — no
behavioural change.
Release: https://github.com/VaiYav/speckit-product-forge/releases/tag/v1.5.1
xargs re-parses stdin as shell tokens, causing 'unterminated quote'
errors when feature descriptions contain apostrophes, double quotes,
or backslashes. Replace with sed-based whitespace trim that preserves
input verbatim.
Add regression tests for special characters in descriptions (core and
extension scripts), plus a negative test for whitespace-only input.
Fixes#2339
* feat: register jira preset in community catalog
Adds luno/spec-kit-preset-jira — overrides speckit.taskstoissues to
create Jira issues instead of GitHub Issues.
See #2223 for context on why this is a preset rather than an extension.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use immutable tag URL and sort jira preset alphabetically
- Change download_url from heads/main to refs/tags/v1.0.0 for reproducible installs
- Move jira entry to correct alphabetical position in presets object
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Ed Harrod <1381991+echarrod@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: bump version to 0.8.0
* chore: begin 0.8.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: rebase onto upstream/main, resolve conflicts with PR #2189
upstream/main merged PR #2189 (wrap-only strategy) which overlaps with
our comprehensive composition strategies (prepend/append/wrap). Resolved
conflicts keeping our implementation as source of truth:
- README: keep our future considerations (composition is now fully
implemented, not a future item)
- presets.py: keep our composition architecture (_reconcile_composed_commands,
collect_all_layers, resolve_content) while preserving #2189's
_substitute_core_template which is used by agents.py for skill
generation
- tests: keep both test sets (our composition tests + #2189's wrap
tests), removed TestReplayWrapsForCommand and
TestInstallRemoveWrapLifecycle which test the superseded
_replay_wraps_for_command API; our composition tests cover equivalent
scenarios
- Restored missing _unregister_commands call in remove() that was lost
during #2189 merge
* fix: re-create skill directory in _reconcile_skills after removal
After _unregister_skills removes a skill directory, _register_skills
skips writing because the dir no longer passes the is_dir() check.
Fix by ensuring the skill subdirectory exists before calling
_register_skills so the next winning preset's content gets registered.
Fixes the Claude E2E failure where removing a top-priority override
preset left skill-based agents without any SKILL.md file.
* fix: address twenty-third round of Copilot PR review feedback
- Protect reconciliation in remove(): wrap _reconcile_composed_commands
and _reconcile_skills in try/except so failures emit a warning instead
of leaving the project in an inconsistent state
- Protect reconciliation in install(): same pattern for post-install
reconciliation so partial installs don't lack cleanup
- Inherit scripts/agent_scripts from base frontmatter: when composing
commands, merge scripts and agent_scripts keys from the base command's
frontmatter into the top layer's frontmatter if missing, preventing
composed commands from losing required script references
- Add tier-5 bundled core fallback to collect_all_layers(): check the
bundled core_pack (wheel) or repo-root templates (source checkout) when
.specify/templates/ doesn't contain the core file, matching resolve()'s
tier-5 fallback so composition can always find a base layer
* fix: address twenty-fourth round of Copilot PR review feedback
- Use yaml.safe_load for frontmatter parsing in resolve_content instead
of CommandRegistrar.parse_frontmatter which uses naive find('---',3);
strip strategy key from final frontmatter to prevent leaking internal
composition directives into rendered agent command files
- Filter _reconcile_skills to specific commands: use _FilteredManifest
wrapper so only the commands being reconciled get their skills updated,
preventing accidental overwrites of other commands' skills that may be
owned by higher-priority presets
* fix: address twenty-fifth round of Copilot PR review feedback
- Support legacy command-frontmatter strategy: when preset.yml doesn't
declare a strategy, check the command file's YAML frontmatter for
strategy: wrap as a fallback so legacy wrap presets participate in
composition and multi-preset chaining
- Guard skill dir creation in _reconcile_skills: only re-create the
skill directory if the skill was previously managed (listed in some
preset's registered_skills), avoiding creation of new skill dirs
that _register_skills would normally skip
* fix: add explanatory comment to empty except in legacy frontmatter parsing
* fix: address twenty-sixth round of Copilot PR review feedback
- Unregister stale commands when composition fails: when resolve_content
returns None during reconciliation (base layer removed), unregister
the command from non-skill agents and emit a warning
- Load extension aliases during reconciliation: _register_command_from_path
now checks extension.yml for aliases when the winning layer is an
extension, so alias files are restored after preset removal
- Use line-based fence detection for legacy frontmatter strategy fallback:
scan for --- on its own line instead of split('---',2) to avoid
mis-parsing YAML values containing ---
* fix: address twenty-seventh round of Copilot PR review feedback
- Handle non-preset winners in _reconcile_skills: when the winning
layer is core/extension/project-override, restore skills via
_unregister_skills so skill-based agents stay consistent with the
priority stack
- Update base_frontmatter_text on replace layers: when a higher-priority
replace layer occurs during composition, update both top and base
frontmatter so scripts/agent_scripts inheritance reflects the
effective base beneath the top composed layer
* fix: address twenty-eighth round of Copilot PR review feedback
- Parse only interior lines in _parse_fm_yaml: use lines[1:-1] instead
of filtering all --- lines, preventing corruption when YAML values
contain a line that is exactly ---
- Omit empty frontmatter: skip re-rendering when top_fm is empty dict
to avoid emitting ---/{}/--- for intentionally empty frontmatter
- Update scaffold wrap comment: mention both {CORE_TEMPLATE} and
$CORE_SCRIPT placeholders for templates/commands vs scripts
- Clarify shell composition scope in ARCHITECTURE.md: note that bash/PS1
resolve_template_content only handles templates; command/script
composition is handled by the Python resolver
* fix: address twenty-ninth round of Copilot PR review feedback
- Fix TestCollectAllLayers docstring: reference collect_all_layers()
- Add default/unknown strategy handling in bash/PS1 composition: error
on unrecognized strategy values instead of silently skipping
- Fix comment: .composed/ is a persistent dir, not temporary
- Fix comment: legacy fallback checks all valid strategies, not just wrap
- Cache PresetRegistry in _reconcile_skills: build presets_by_priority
once instead of constructing registry per-command
* fix: address thirtieth round of Copilot PR review feedback
- Guard legacy frontmatter fallback: only check command file frontmatter
for strategy when the manifest entry doesn't explicitly include the
strategy key, preventing override of manifest-declared strategies
- Document rollback limitation: note that mid-registration failures may
leave orphaned agent command files since partial progress isn't
captured by the local vars
* fix: handle project override skills and extension context in reconciliation
* fix: add comment to empty except in extension registration fallback
* fix: filter extension commands in reconciliation and fix type annotation
* fix: filter extension commands from post-install reconciliation
Apply the same extension-installed check used in _register_commands to
the reconciliation command list, preventing reconciliation from
registering commands for extensions that are not installed.
* fix: skip convention fallback for explicit file paths and add stem fallback to tier-5
When a preset manifest provides an explicit file path that does not
exist, skip the convention-based fallback to avoid masking typos.
Also add speckit.<stem> to <stem>.md fallback in tier-5 bundled/source
core lookup for consistency with tier-4.
* fix: scan past non-replace layers to find base in resolve_content
The base-finding scan now skips non-replace layers below a replace
layer instead of stopping at the first non-replace. This fixes the
case where a low-priority append/prepend layer sits below a replace
that should serve as the base for composition.
* fix: add context_note to non-skill agent registration for extensions
Add context_note parameter to register_commands_for_non_skill_agents
and pass extension name/id during reconciliation so rendered command
files preserve the extension-specific context markers.
* fix: Optional type, rollback safety, and override skill restoration
- Fix context_note type to Optional[str]
- Wrap shutil.rmtree in try/except during install rollback
- Separate override-backed skills from core/extension in _reconcile_skills
* fix: align bash/PS1 base-finding with Python resolver
Rewrite bash and PowerShell composition loops to find the effective
base replace layer first (scanning bottom-up, skipping non-replace
layers below it), then compose only from the base upward. This
prevents evaluation of irrelevant lower layers (e.g. a wrap with
no placeholder below a replace) and matches resolve_content behavior.
* fix: PS1 no-python warning, integration hook for override skills, alias cleanup
- Warn when no Python 3 found in PS1 and presets use composition strategies
- Apply post_process_skill_content integration hook when restoring
override-backed skills so agent-specific flags are preserved
- Unregister command aliases alongside primary name when composition
fails to prevent orphaned alias files
* fix: include aliases in removed_cmd_names during preset removal
Read aliases from preset manifest before deleting pack_dir so alias
command files are included in unregistration and reconciliation.
* fix: add comment to empty except in alias extraction during removal
* fix: scan top-down for effective base in all resolvers
Change base-finding to scan from highest priority downward to find the
nearest replace layer, then compose only layers above it. Prevents
evaluation of irrelevant lower layers (e.g. a wrap without placeholder
below a higher-priority replace) across Python, bash, and PowerShell.
* fix: align CLI composition chain display with top-down base-finding
Show only contributing layers (base and above) in preset resolve
output, matching resolve_content top-down semantics. Layers below
the effective base are omitted since they do not contribute.
* fix: guard corrupted registry entries and make manifest authoritative
- Add isinstance(meta, dict) guard in bash registry parsing so corrupted
entries are skipped instead of breaking priority ordering
- Only use convention-based file lookup when the manifest does not list
the requested template, making preset.yml authoritative and preventing
stray on-disk files from creating unintended layers
* fix: align resolve() with manifest file paths and match extension context_note
- Update resolve() preset tier to consult manifest file paths before
convention-based lookup, matching collect_all_layers behavior
- Use exact extension context_note format matching extensions.CommandRegistrar
- Update test to declare template in manifest (authoritative manifest)
* revert: restore resolve() convention-based behavior for backwards compatibility
resolve() is the existing public API used by shell scripts and other
callers. Changing it to manifest-authoritative breaks backward compat
for presets that rely on convention-based file lookup. Only the new
collect_all_layers/resolve_content path uses manifest-authoritative
logic.
* fix: only pre-compose when this preset is the top composing layer
Skip composition in _register_commands when a higher-priority replace
layer already wins for the command. Register the raw file instead and
let reconciliation write the correct final content.
* fix: deduplicate PyYAML warnings and use self.registry in reconciliation
- Emit PyYAML-missing warning once per function call in bash/PS1 instead
of per-preset to avoid spamming stderr
- Use self.registry.list_by_priority() in reconciliation methods instead
of constructing new PresetRegistry instances to avoid redundant I/O
and potential consistency issues
* fix: document strategy handling consistency between layers and registrar
Composed output already strips strategy from frontmatter (resolve_content
pops it). Raw file registration preserves legacy frontmatter strategy
for backward compat; reconciliation corrects the final state.
* fix: correct stale comments for alias tracking and base-finding algorithm
* security: validate manifest file paths in bash/PowerShell resolvers
Reject absolute paths and parent directory traversal (..) in the
manifest-declared file field before joining with the preset directory.
Matches the Python-side validation in PresetManifest._validate().
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
* Initial plan
* feat(copilot): add --skills flag for skills-based scaffolding
Add --skills integration option to CopilotIntegration that scaffolds
commands as speckit-<name>/SKILL.md under .github/skills/ instead of
the default .agent.md + .prompt.md layout.
- Add options() with --skills flag (default=False)
- Branch setup() between default and skills modes
- Add post_process_skill_content() for Copilot-specific mode: field
- Adjust build_command_invocation() for skills mode (/speckit-<stem>)
- Update dispatch_command() with skills mode detection
- Parse --integration-options during init command
- Add 22 new skills-mode tests
- All 15 existing default-mode tests continue to pass
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/a4903fab-64ff-46c3-8eb8-a47f495a70c0
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* docs(AGENTS.md): document Copilot --skills option
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/a4903fab-64ff-46c3-8eb8-a47f495a70c0
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* Potential fix for pull request finding 'Unused local variable'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: address PR #2324 review feedback
- Reset _skills_mode at start of setup() to prevent singleton state leak
- Tighten skills auto-detection to require speckit-*/SKILL.md (not any
non-empty .github/skills/ directory)
- Add copilot_skill_mode to init next-steps so skills mode renders
/speckit-plan instead of /speckit.plan
- Fix docstring quoting to match actual unquoted output
- Add 4 tests covering singleton reset, auto-detection false positive,
speckit layout detection, and next-steps skill syntax
- Fix skipped test_invalid_metadata_error_returns_unknown by simulating
InvalidMetadataError on Python versions that lack it
* fix: inline skills prompt in dispatch_command auto-detection path
build_command_invocation() reads self._skills_mode which stays False
when skills mode is only auto-detected from the project layout. Inline
the /speckit-<stem> prompt construction so dispatch_command() sends the
correct prompt regardless of how skills mode was detected.
Also strengthen test_dispatch_detects_speckit_skills_layout to assert
the -p prompt contains /speckit-plan and the user args.
---------
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>
* docs(install): add pipx as alternative installation method
- Add pipx commands to README.md installation section
- Add note about pipx compatibility to docs/installation.md
- Mention pipx persistent installation in docs/quickstart.md
- Add pipx upgrade instructions to docs/upgrade.md
- Clarify that project has no uv-specific dependencies
Refs: https://github.com/github/spec-kit/discussions/2255
* docs(install): address Copilot feedback - update prerequisites and upgrade references for pipx
* Update docs/quickstart.md
markdownlint’s MD012 (enabled in this repo) flags multiple consecutive blank lines
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update docs/upgrade.md
In the Quick Reference table, the label “pipx upgrade” is misleading because the command shown is `pipx install --force ...` (a reinstall). by copilot.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: --force now overwrites shared infra files during init and upgrade
_install_shared_infra() previously skipped all existing files under
.specify/scripts/ and .specify/templates/, regardless of --force.
This meant users could never receive upstream fixes to shared scripts
or templates after initial project setup.
Changes:
- Add force parameter to _install_shared_infra(); when True, existing
files are overwritten with the latest bundled versions
- Wire force=True through specify init --here --force and
specify integration upgrade --force call sites
- Replace hidden logging.warning with visible console output listing
skipped files and suggesting --force
- Fix contradictory upgrade docs that claimed --force updated shared
infra (it didn't) and warned about overwrites (they didn't happen)
- Add 6 tests: unit tests for skip/overwrite/warning behavior, plus
end-to-end CLI tests for both --force and non-force paths
Fixes#2319
* fix: improve skip warning to suggest specific commands
Address review feedback: the generic '--force' suggestion was
misleading when _install_shared_infra is called from integration
install/switch (which don't have a --force for shared infra).
Now points users to the specific commands that can refresh shared
infra: 'specify init --here --force' or 'specify integration
upgrade --force'.
* chore: bump version to 0.7.5
* chore: begin 0.7.6.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(cli): add specify self check and self upgrade stub (#2282)
Introduce a new `specify self` Typer sub-app with two subcommands.
`specify self check` performs a read-only lookup against the GitHub Releases
API, compares the installed version to the latest tag with PEP 440 semantics,
and prints one of four verdicts (newer-available, up-to-date, indeterminate,
graceful-failure). When a newer stable release is available, the output
includes a copy-pasteable `uv tool install --force --from git+...@<tag>`
reinstall command. `GH_TOKEN` / `GITHUB_TOKEN` is attached as a bearer
credential when set so users behind shared IPs escape the anonymous 60/hour
rate limit.
`specify self upgrade` is a documented non-destructive stub in this release:
three-line guidance output, exit 0, no outbound call, no install-method
detection. The real destructive implementation is planned as follow-up work.
Failure categorization is a fixed three-entry enum (offline or timeout,
rate limited, HTTP <code>). Anything outside those three categories
propagates as a non-zero exit so bugs surface instead of being silently
swallowed. No machine-readable output, no retries, no caching in this
release — see issue #2282 discussion.
Tests mock `urllib.request.urlopen`; the suite performs zero real network
calls. Full regression suite: 1586 passed.
* fix(cli): disable Rich highlight for deterministic output
Rich's default `highlight=True` applies ANSI color to detected patterns
(integers, version strings, paths) whenever stdout is deemed a TTY.
This caused intermittent failures in existing pytest assertions in
tests/test_cli_version.py and tests/test_extensions.py::TestExtensionRemoveCLI
that compare plain-text output without passing through `strip_ansi()`.
Setting `Console(highlight=False)` globally makes all CLI output
deterministic and fixes the flake without modifying the affected tests.
The numeric cyan highlighting was not a documented part of the CLI
visual contract.
* fix: address copilot review feedback
* fix: tighten self-check token handling
* fix: align self-check helpers and script metadata
* fix: harden self-check version handling
* fix: guard self-check failure rendering
Move the community presets table from the main README to a dedicated
docs/community/presets.md page, matching the pattern used for
walkthroughs and friends.
- Add docs/community/presets.md with the full presets table
- Add Claude AskUserQuestion preset (was in catalog but missing from table)
- Add Presets entry to docs/toc.yml under Community
- Replace inline README table with a short link to the docs page
* catalog: add wireframe extension
Adds https://github.com/TortoiseWolfe/spec-kit-extension-wireframe
(v0.1.0) to the community catalog. Provides a visual feedback loop
for spec-driven development: SVG wireframe generation, review, and
sign-off. Approved wireframes become spec constraints honored by
/plan, /tasks, and /implement.
Supersedes #1410 — the old PR predated the extension system
introduced in #2130 and proposed commands in templates/commands/,
which is no longer the right home for third-party commands.
* catalog: address review feedback (position + author)
Two changes per Copilot review:
- Move `wireframe` entry alphabetically between `whatif` and
`worktree` (was appended after `worktrees`).
- Simplify `author` from "TortoiseWolfe (turtlewolfe.com)" to
just "TortoiseWolfe" so the exact-match author filter in
`ExtensionCatalog.search` finds the entry. Portfolio URL
remains accessible via `homepage`/`repository`.
Thanks @Copilot, @mnriem for the review.
* docs(readme): add Wireframe Visual Feedback Loop row
Addresses @mnriem's follow-up: the README extension table also
needs an entry, not just the catalog JSON. Slots in alphabetically
between "What-if Analysis" and "Worktree Isolation" with category
`visibility` and Read+Write effect (since sign-off writes the
approved wireframe paths into spec.md).
* catalog: use speckit-prefixed command names in wireframe description
Address remaining Copilot review comment on PR #2262. The actual
commands are /speckit.plan, /speckit.tasks, /speckit.implement;
the unprefixed names would mislead catalog users.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* catalog: bump wireframe extension to v0.1.1
v0.1.1 of spec-kit-extension-wireframe ships the /speckit.-prefixed
command references in extension.yml and README.md. This updates the
catalog entry to point at the new release tag so `specify extension
add wireframe` installs the corrected version.
* catalog: set wireframe created_at to current timestamp
Per EXTENSION-PUBLISHING-GUIDE.md: newly added entries should use
the current timestamp for both created_at and updated_at. The 04-17
value reflected when I drafted the entry locally, not when the
catalog submission landed.
---------
Co-authored-by: TortoiseWolfe <jonpohlner@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Move community walkthroughs from README to docs/community
Extract the community walkthroughs section from README.md into its own
docs/community/walkthroughs.md file and replace it with a short summary
linking to the GitHub Pages URL.
* Address review: fix double-See phrasing, add walkthroughs to docs nav
Follow-up to #2306 (merged). Per maintainer request
(https://github.com/github/spec-kit/pull/2306#issuecomment-4296655643),
adds the red-team entry to the alphabetically-ordered community-extensions
table in README.md so the extension is discoverable alongside the other
community entries — not only via catalog.community.json.
Slotted alphabetically between "Reconcile Extension" and "Repository
Index". Category: docs. Effect: Read+Write (produces a structured
findings-report file at specs/<feature-id>/red-team-findings-*.md; does
not modify specs — every resolution is maintainer-authorised).
Co-authored-by: Ash Brener <ashley@midletearth.com>
* feat(catalog): add red-team extension
Adds the `red-team` community extension to the catalog:
- Adversarial review of functional specs before /speckit.plan locks in
architecture.
- Complements /speckit.clarify (correctness) and /speckit.analyze
(consistency) with parallel adversarial lens agents.
- One command: speckit.red-team.run
- MIT licensed; requires spec-kit >= 0.7.0.
Origin: this extension was originally proposed as a core command
(github/spec-kit#2303). Per maintainer guidance (mnriem's comment on
that PR), it's been restructured as a community extension hosted at
https://github.com/ashbrener/spec-kit-red-team.
Dogfood-validated on a 500-line functional spec: 5 lens agents
dispatched in parallel returned 25 findings in ~1.5 min wall-clock,
19 of which met the meaningful-finding bar (severity >= HIGH AND
novel adversarial angle that clarify/analyze structurally cannot
catch). Full detail in the extension's CHANGELOG.
* catalog: shorten red-team description to fit <200 char schema limit
Resolves Copilot review comment on #2306. Previous description (259
chars) exceeded the extensions/EXTENSION-PUBLISHING-GUIDE.md Appendix
schema ceiling. Shortened to 188 chars, keeping the distinctive
value proposition (adversarial, complements clarify/analyze) and
moving the per-phase mechanics to the extension's own README.
* catalog: bump red-team to v1.0.1 (lower required spec-kit version)
Follow-up to v1.0.0 catalog entry:
- version: 1.0.0 -> 1.0.1
- download_url: points at v1.0.1 release asset
- requires.speckit_version: >=0.7.0 -> >=0.1.0
The v1.0.0 requirement was too strict and blocked installation on
common 0.6.x field versions (confirmed via local install attempt).
The extension uses no 0.7.x-specific APIs; matches community norm
(reconcile, refine, others use >=0.1.0).
* catalog: bump red-team to v1.0.2 (adds mandatory before_plan gate)
v1.0.2 ships a /speckit.red-team.gate command wired as a mandatory
before_plan hook so /speckit.plan auto-invokes it on every run against
qualifying specs. Non-qualifying specs return PROCEED silently; qualifying
specs without findings on record return HALT with explicit remediation
(run /speckit.red-team.run, or opt out via --skip-red-team-gate: <reason>
which is recorded as an Accepted Risk [red-team-skipped] in the plan).
Catalog metadata delta:
- version: 1.0.1 -> 1.0.2
- download_url: v1.0.2/red-team-v1.0.2.zip
- provides.commands: 1 -> 2 (adds speckit.red-team.gate)
- provides.hooks: 0 -> 1 (adds before_plan hook)
No breaking changes. Projects that do not want the gate simply do not
install the extension.
---------
Co-authored-by: Ash Brener <ashley@midletearth.com>
* Add superpowers-bridge community extension
Adds the superpowers-bridge extension by WangX0111 to the community
catalog and README table. This extension bridges spec-kit with
obra/superpowers (brainstorming, TDD, subagent-driven-development,
code-review) into a unified, resumable workflow with graceful
degradation and session progress tracking.
Extension details:
- ID: superpowers-bridge
- Repository: https://github.com/WangX0111/superspec
- Version: 1.0.0
- Commands: 5, Hooks: 3
- License: MIT
* Address Copilot review feedback
- Update top-level updated_at to 2026-04-22
- Shorten description to under 200 characters
---------
Co-authored-by: 乘浩 <wch453799@alibaba-inc.com>
* feat: implement strategy: wrap
* fix: resolve merge conflict for strategy wrap correctness
* feat: multi-preset composable wrapping with priority ordering
Implements comment #4 from PR review: multiple installed wrap presets
now compose in priority order rather than overwriting each other.
Key changes:
- PresetResolver.resolve() gains skip_presets flag; resolve_core() wraps
it to skip tier 2, preventing accidental nesting during replay
- _replay_wraps_for_command() recomposed all enabled wrap presets for a
command in ascending priority order (innermost-first) after any
install or remove
- _replay_skill_override() keeps SKILL.md in sync with the recomposed
command body for ai-skills-enabled projects
- install_from_directory() detects strategy: wrap commands, stores
wrap_commands in the registry entry, and calls replay after install
- remove() reads wrap_commands before deletion, removes registry entry
before rmtree so replay sees post-removal state, then replays
remaining wraps or unregisters when none remain
Tests: TestResolveCore (5), TestReplayWrapsForCommand (5),
TestInstallRemoveWrapLifecycle (5), plus 2 skill/alias regression tests
* fix: resolve extension commands via manifest file mapping
PresetResolver.resolve_extension_command_via_manifest() consults each
installed extension.yml to find the actual file declared for a command
name, rather than assuming the file is named <cmd_name>.md. This fixes
_substitute_core_template for extensions like selftest where the manifest
maps speckit.selftest.extension → commands/selftest.md.
Resolution order in _substitute_core_template is now:
1. resolve_core(cmd_name) — project overrides win, then name-based lookup
2. resolve_extension_command_via_manifest(cmd_name) — manifest fallback
3. resolve_core(short_name) — core template short-name fallback
Path traversal guard mirrors the containment check already present in
ExtensionManager to reject absolute paths or paths escaping the extension
root.
* fix: add bundled core_pack as Priority 5 in PresetResolver.resolve()
resolve_core() was returning None for built-in commands (implement,
specify, etc.) because PresetResolver only checked .specify/templates/
commands/ (Priority 4), which is never populated for commands in a
normal project. strategy:wrap presets rely on resolve_core() to fetch
the {CORE_TEMPLATE} body, so the wrap was silently skipped and SKILL.md
was never updated.
Priority 5 now checks core_pack/commands/ (wheel install) or
repo_root/templates/commands/ (source checkout), mirroring the pattern
used by _locate_core_pack() elsewhere.
Updated two tests whose assertions assumed resolve_core() always
returned None when .specify/templates/commands/ was absent.
* fix: harden preset wrap replay removal
* fix: stabilize existing directory error output
* fix: track outermost_pack_id from contributing preset; use Path.parts in tests
- outermost_pack_id now updates alongside outermost_frontmatter inside
the wrap loop, so it reflects the actual last contributing preset
rather than always taking wrap_presets[0] (which may have been skipped)
- Replace str(path) substring checks in TestResolveCore with Path.parts
tuple comparisons for correct behaviour on Windows (CI runs windows-latest)
* fix: guard against non-mapping YAML manifests; apply integration post-processing in replay
- ExtensionManifest._load raises ValidationError for non-dict YAML roots instead of TypeError
- PresetManager._replay_wraps_for_command calls integration.post_process_skill_content,
matching _register_skills behaviour
- PresetResolver skips extensions that raise OSError/TypeError/AttributeError on manifest load
- Tests: non-mapping YAML, OSError manifest skip, and replay integration post-processing
Extend the alias containment guard from b67b285 to the two remaining
write paths that derive filenames from free-form command/alias names:
- Primary command write in CommandRegistrar.register_commands()
- CommandRegistrar.write_copilot_prompt()
Consolidate the check into a shared _ensure_inside() helper. Per
maintainer guidance on #2229, use a lexical
(os.path.normpath + Path.is_relative_to) containment check rather than
resolve() so `..` / absolute-path traversal is rejected while
intentionally symlinked sub-directories under an agent's commands
directory (e.g. .claude/skills/shared -> /team/shared-skills) keep
working for existing extension setups.
Add 22 parametrised regression cases covering traversal payloads on
primary commands, aliases, and the Copilot companion prompt, plus a
positive case that confirms symlinked sub-directories remain supported.
* chore: bump version to 0.7.4
* chore: begin 0.7.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(copilot): use --yolo to grant all permissions in non-interactive mode
The Copilot CLI's --allow-all-tools flag only covers tool execution
permissions but does not grant path or URL access. When the Copilot
agent autonomously runs shell commands (e.g. npm run build) during
workflow execution, the CLI blocks path access and cannot prompt for
approval in non-interactive mode, producing:
Permission denied and could not request permission from user
Replace --allow-all-tools with --yolo (equivalent to --allow-all-tools
--allow-all-paths --allow-all-urls) to grant all three permission types.
Rename the opt-out env var from SPECKIT_ALLOW_ALL_TOOLS to
SPECKIT_COPILOT_ALLOW_ALL to match the formal --allow-all alias and
scope it to the Copilot integration.
Fixes#2294
* review: deprecate SPECKIT_ALLOW_ALL_TOOLS, rename to SPECKIT_COPILOT_ALLOW_ALL_TOOLS
Address Copilot review feedback:
- Honour the old SPECKIT_ALLOW_ALL_TOOLS env var as a fallback with a
DeprecationWarning so existing opt-outs are not silently ignored.
- Rename the new canonical env var to SPECKIT_COPILOT_ALLOW_ALL_TOOLS.
- New var takes precedence when both are set.
- Use monkeypatch in tests to avoid flakiness from ambient env vars.
- Add tests for deprecation warning, precedence, and opt-out paths.
* review: use UserWarning instead of DeprecationWarning
DeprecationWarning is suppressed by default in Python, so users relying
on the old SPECKIT_ALLOW_ALL_TOOLS env var would never see the
deprecation notice during normal CLI runs. Switch to UserWarning which
is always shown. Update test to also assert the warning category.
* feat: add CITATION.cff and .zenodo.json for academic citation support
Adds a Citation File Format file (CITATION.cff) so GitHub surfaces a
native "Cite this repository" button, and a .zenodo.json metadata file
so Zenodo can pre-fill the DOI record once a maintainer enables the
integration at zenodo.org.
Closes#2269
* fix: address PR review feedback on citation metadata
- Fix 'a specify CLI' -> 'the Specify CLI' in both files
- Broaden description to include extensions, presets, and workflows
- Remove empty orcid fields from .zenodo.json creators
- Update date-released to 2026-04-17 (actual 0.7.3 release date)
* fix: correct docs URL in CITATION.cff to github.io domain
* Add spec-validate to community catalog
- Extension ID: spec-validate
- Version: 1.0.1
- Author: Ahmed Eltayeb
- Description: Comprehension validation, review gating, and approval state for spec-kit artifacts
* Reorder spec-validate before speckit-utils (address Copilot feedback)
Lexicographically 'spec-validate' < 'speckit-utils' because '-' (0x2D)
sorts before 'k' (0x6B). Move the entry to match the alphabetical
ordering used in the 's' range of the catalog.
* Add version-guard to community catalog
- Extension ID: version-guard
- Version: 1.0.0
- Author: KevinBrown5280
- Description: Verify tech stack versions against live registries before planning and implementation
* Fix alphabetical ordering: move Version Guard after Verify rows
- Extension ID: spec-reference-loader
- Version: 1.0.0
- Author: KevinBrown5280
- Description: Reads the ## References section from the current feature spec and loads the listed files into context
* Update preset-fiction-book-writing to community catalog
- Preset ID: fiction-book-writing
- Version: 1.5.0
- Author: Andreas Daumann
- Description: Spec-Driven Development for novel and long-form fiction. Replaces software engineering terminology with storytelling craft: specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports 8 POV modes, all major plot structure frameworks, 5 humanized-AI prose profiles, and exports to DOCX/EPUB/LaTeX via pandoc. V1.5.0: Support interactive, audiobooks, series, workflow corrections
* Add fiction-book-writing preset to community catalog
- Preset ID: fiction-book-writing
- Version: 1.6.0
- Author: Andreas Daumann
- Description: Added support for 12 languages, export with templates, cover builder, bio builder, workflow fixes
* Update presets/catalog.community.json
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fixed update_at for fiction-book-writing preset
* Update README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fixed description for fiction-book-writing
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.7.3
* chore: begin 0.7.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Replace shell-based context updates with marker-based upsert
Replace ~3500 lines of bash/PowerShell agent context update scripts
with a Python-based approach using <!-- SPECKIT START/END --> markers.
IntegrationBase now manages the agent context file directly:
- upsert_context_section(): creates or updates the marked section at
init/install/switch time with a directive to read the current plan
- remove_context_section(): removes the section at uninstall, deleting
the file only if it becomes empty
- __CONTEXT_FILE__ placeholder in command templates is resolved per
integration so the plan command references the correct agent file
- context_file is persisted in init-options.json for extension access
The plan command template instructs the LLM to update the plan
reference between the markers in the agent context file.
Removed:
- scripts/bash/update-agent-context.sh (857 lines)
- scripts/powershell/update-agent-context.ps1 (515 lines)
- 56 integration wrapper scripts (update-context.sh/.ps1)
- templates/agent-file-template.md
- agent_scripts frontmatter key and {AGENT_SCRIPT} replacement logic
- update-context reference from integration.json
- tests/test_cursor_frontmatter.py (tested deleted scripts)
Added:
- upsert/remove context section methods on IntegrationBase
- __CONTEXT_FILE__ placeholder support in process_template()
- context_file field in init-options.json (init/switch/uninstall)
- Per-integration tests: context file correctness, plan reference,
init-options persistence (78 new context_file tests)
- End-to-end CLI validation across all 28 integrations
* fix: search for end marker after start marker in context section methods
Address Copilot review: content.find(CONTEXT_MARKER_END) searched from
the start of the file rather than after the located start marker. If
the file contained a stray end marker before the start marker, the
wrong slice could be replaced.
Now both upsert_context_section() and remove_context_section() pass
start_idx as the second argument to find() and validate end_idx >
start_idx before performing the replacement.
* fix: address Copilot review feedback on context section handling
1. Fix grammar in _build_context_section() directive text — add commas
for a complete sentence.
2. Resolve __CONTEXT_FILE__ in resolve_skill_placeholders() — skills
generated via extensions/presets for codex/kimi now replace the
placeholder using the context_file value from init-options.json.
3. Handle Cursor .mdc frontmatter — when creating a new .mdc context
file, prepend alwaysApply: true YAML frontmatter so Cursor
auto-loads the rules.
4. Fix empty-file leading newline — when the context file exists but
is empty, write the section directly instead of prepending a blank
line.
* fix: address second round of Copilot review feedback
1. Ensure .mdc frontmatter on existing files — upsert_context_section()
now checks for missing YAML frontmatter on .mdc files during updates
(not just creation), so pre-existing Cursor files get alwaysApply.
2. Guard against context_file=None — use 'or ""' instead of a default
arg so explicit null values in init-options.json don't cause a
TypeError in str.replace().
3. Clean up .mdc files on removal — remove_context_section() treats
files containing only the Speckit-generated frontmatter block as
empty, deleting them rather than leaving orphaned frontmatter.
* fix: address third round of Copilot review feedback
1. CRLF-safe .mdc frontmatter check — use lstrip().startswith('---')
instead of startswith('---\n') so CRLF files don't get duplicate
frontmatter.
2. CRLF-safe .mdc removal check — normalize line endings before
comparing against the sentinel frontmatter string.
3. Call remove_context_section() during integration_uninstall() — the
manifest-only uninstall was leaving the managed SPECKIT markers
behind in the agent context file.
4. Fix stale docstring — remove 'agent_scripts' mention from
test_lean_commands_have_no_scripts().
* fix: address fourth round of Copilot review feedback
1. Remove unused script_type parameter from _write_integration_json()
and all 3 call sites — the parameter was no longer referenced after
the update-context script removal.
2. Fix _build_context_section() docstring — correct example path from
'.specify/plans/plan.md' to 'specs/<feature>/plan.md'.
3. Improve .mdc frontmatter-only detection in remove_context_section()
— use regex to match any YAML frontmatter block (not just the exact
Speckit-generated one), so .mdc files with additional frontmatter
keys are also cleaned up when no body content remains.
* fix: handle corrupted markers and parse .mdc frontmatter robustly
1. Handle partial/corrupted markers in upsert_context_section() —
if only the START marker exists (no END), replace from START
through EOF. If only the END marker exists, replace from BOF
through END. This keeps upsert idempotent even when a user
accidentally deletes one marker.
2. Parse .mdc YAML frontmatter properly — new _ensure_mdc_frontmatter()
helper parses existing frontmatter and ensures alwaysApply: true is
set, rather than just checking for the --- delimiter. Handles
missing frontmatter, existing frontmatter without alwaysApply, and
already-correct frontmatter.
* fix: preserve .mdc frontmatter, add tests, clean up on switch
1. Rewrite _ensure_mdc_frontmatter() with regex — preserves comments,
formatting, and custom keys in existing frontmatter instead of
destructively re-serializing via yaml.safe_dump(). Inserts or
fixes alwaysApply: true in place.
2. Add 6 focused .mdc frontmatter tests to cursor-agent test file:
new file creation, missing frontmatter, preserved custom keys,
wrong alwaysApply value, idempotent upserts, removal cleanup.
3. Call remove_context_section() during integration switch Phase 1 —
prevents stale SPECKIT markers from being left in the old
integration's context file. Also clear context_file from
init-options during the metadata reset.
* fix: remove unused MDC_FRONTMATTER, preserve inline comments, normalize bare CR
1. Remove unused MDC_FRONTMATTER class variable — dead code after
_ensure_mdc_frontmatter() was rewritten with regex.
2. Preserve inline comments when fixing alwaysApply — the regex
substitution now captures trailing '# comment' text and keeps it.
3. Normalize bare CR in upsert_context_section() — match the
behavior of remove_context_section() which already normalizes
both CRLF and bare CR.
4. Clarify .mdc removal comment — 'treat frontmatter-only as empty'
instead of misleading 'strip frontmatter'.
* fix: handle corrupted markers in remove, CRLF-safe end-marker consumption
1. Handle corrupted markers in remove_context_section() — mirror
upsert's behavior: start-only removes start→EOF, end-only removes
BOF→end. Previously bailed out leaving partial markers behind.
2. CRLF-safe end-marker consumption — both upsert and remove now
handle \r\n after the end marker, not just \n. Prevents extra
blank lines at replacement boundaries in CRLF files.
3. Clarify path rule in plan template — distinguish filesystem
operations (absolute paths) from documentation/agent context
references (project-relative paths).
* fix: only remove context section when both markers are well-ordered
remove_context_section() previously treated mismatched markers as
corruption and aggressively removed from BOF→end-marker or
start-marker→EOF, which could delete user-authored content if only
one marker remained. Now it only removes when both START and END
markers exist and are properly ordered, returning False otherwise.
Move the Community Friends section from the main README into a dedicated
docs page at docs/community/friends.md, following the same structure as
the Reference section.
- New: docs/community/friends.md with content from README
- Updated: docs/toc.yml with Community section and Friends entry
- Updated: docs/docfx.json to include community/*.md in content glob
- Updated: README.md to link to the new docs page instead of inline list
- Adds scope entry to catalog.community.json (between review and security-review)
- Adds Spec Scope row to community extensions table in README.md (between Spec Refine and Spec Sync)
- Bumps top-level updated_at to 2026-04-16T19:00:00Z
* docs: add Claude Code / Copilot plugin installation option
Add Option 4 to README installation section documenting plugin-based installation via Claude Code and Copilot CLI marketplace commands
* docs(readme): move cc-spec-kit plugin to Community Friends
Relocate the cc-spec-kit plugin reference to the Community Friends
* fix: suppress CRLF warnings in auto-commit.ps1 (#2253)
Replace 2> with 2>&1 redirection and assignment to properly
suppress stderr output including CRLF warnings on Windows. Exit code
logic preserved for change detection.
Fixes#2253
* fix: use SilentlyContinue for CRLF stderr handling, add tests
The 2>&1 approach still raises terminating errors under
$ErrorActionPreference='Stop'. Instead, temporarily set
SilentlyContinue around all native git calls that may emit
CRLF warnings to stderr (rev-parse, diff, ls-files, add, commit).
Adds 5 pytest tests (TestAutoCommitPowerShellCRLF) that set
core.autocrlf=true with LF-ending files. On Windows runners
this triggers actual CRLF warnings; on other platforms the tests
pass trivially.
Fixes#2253
* refactor: address Copilot review feedback
- Use 'Continue' instead of 'SilentlyContinue' so error output is
still captured in $out for diagnostics on real git failures.
- Wrap all three EAP save/restore blocks in try/finally to guarantee
restoration even on unexpected exceptions.
- Fix CRLF test to commit a tracked LF file first, then modify it,
so git diff --quiet HEAD actually inspects the tracked change and
triggers the CRLF warning on Windows.
* test: assert CRLF warning fires on Windows
On Windows, probe git diff stderr before running the script to verify
the test setup actually produces the expected CRLF warning. This
makes the regression test deterministic on the Windows runner. On
non-Windows the probe is skipped (warnings don't fire there).
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
* chore: bump version to 0.7.2
* chore: begin 0.7.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs: add core commands reference and simplify README CLI section
- New docs/reference/core.md: reference for init (active options only,
copilot as main example), check, and version commands
- docs/toc.yml: add Core Commands under Reference
- README.md: replace verbose CLI Reference section (init options table,
30+ per-agent examples, deprecated flags, env vars) with links to
reference docs; use copilot as main example throughout
* docs: add CLI reference overview page
- New docs/reference/overview.md: explains each CLI surface area
(core, integrations, extensions, presets, workflows) with key
commands and links to detailed reference pages
- docs/toc.yml: add Overview as first item under Reference
- README.md: simplify CLI Reference to single link to overview page
* docs: remove command references from overview, keep paragraphs only
* docs: add workflows reference, reorganize into docs/reference/, and add --version flag
- Move integrations.md, extensions.md, presets.md into docs/reference/
- New docs/reference/workflows.md: command reference for all workflow
commands, built-in SDD Cycle workflow with Mermaid diagram, step types,
expressions, input types, state/resume, and FAQ
- Rename workflow input feature_name to spec with prompt 'Describe what
you want to build' to match speckit.specify command terminology
- Add --version / -V flag to root specify command with tests
- Update docs/toc.yml, README.md links, and docs/upgrade.md cross-reference
to use reference/ paths
- Add workflow command to README CLI reference table
* docs: update speckit_version requirement to >=0.7.2 in workflow example
- New docs/presets.md: command reference for all 9 specify preset commands
and 3 specify preset catalog commands, file resolution stack with Mermaid
diagrams, catalog resolution order, and FAQ
- src/specify_cli/__init__.py: rename pack_id to preset_id across all preset
CLI commands so --help shows PRESET_ID matching the docs
- docs/toc.yml: add Presets under Reference section
- README.md: update presets link to published docs site
- New docs/extensions.md: command reference for all 9 specify extension
commands and 3 specify extension catalog commands, plus catalog
resolution order, extension configuration, and FAQ
- docs/integrations.md: add FAQ section covering single-integration limit,
file preservation, key discovery, CLI vs IDE requirements, upgrade vs switch
- docs/toc.yml: add Extensions under Reference section
- README.md: update integration and extension links to published docs site
- New docs/integrations.md: canonical reference for supported agents table
(with keys), list/install/uninstall/switch/upgrade commands, file
preservation behavior, and integration-specific options
- README.md: replace inline agents table with summary + link to new page;
normalize heading to 'Supported AI Coding Agent Integrations'
- docs/toc.yml: add top-level 'Reference' section with Integrations page
- docs/upgrade.md: fix broken cross-reference, update terminology
- CONTRIBUTING.md: update anchor link to new heading
* Initial plan
* feat: add integration catalog system with catalog files, IntegrationCatalog class, list --catalog flag, upgrade command, integration.yml descriptor, and tests
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/bbcd44e8-c69c-4735-adc1-bdf1ce109184
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* fix: address PR review feedback
- Replace empty except with cache cleanup in _fetch_single_catalog
- Log teardown failure warning instead of silent pass in upgrade
- Validate catalog_data and integrations are dicts before use
- Catch OSError/UnicodeError in IntegrationDescriptor._load
- Add isinstance checks for integration/requires/provides/commands
- Enforce semver (X.Y.Z) instead of PEP 440 for descriptor versions
- Fix docstring and CONTRIBUTING.md to match actual block-on-modified behavior
- Restore old manifest on upgrade failure for transactional safety
* refactor: address second round of PR review feedback
- Remove dead cache_file/cache_metadata_file attributes from IntegrationCatalog
- Deduplicate non-default catalog warning (show once per process)
- Anchor version regex to reject partial matches like 1.0.0beta
- Fix 'Preserved modified' message to 'Skipped' for accuracy
- Make upgrade transactional: install new files first, then remove stale
old-only files, so a failed setup leaves old integration intact
- Update CONTRIBUTING.md: speckit_version validates presence only
* 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: address third round of PR review feedback
- Fix CONTRIBUTING.md JSON examples to show full catalog structure with
schema_version and integrations wrapper
- Wrap cache writes in try/except OSError for read-only project dirs
- Validate _load_catalog_config YAML root is a dict
- Skip non-dict integ_data entries in merged catalog
- Normalize tags to list-of-strings before filtering/searching
- Add path traversal containment check for stale file deletion
- Clarify docstring: lower numeric priority = higher precedence
* fix: address fourth round of PR review feedback
- Remove unused _write_catalog helper from test file
- Fix comment: tests use monkeypatched urlopen, not file:// URLs
- Wrap cache unlink calls in OSError handler
- Add explicit encoding='utf-8' to all cache read_text/write_text calls
- Restore packaging.version.Version for descriptor version validation
to align with extension/preset validators
- Add missing goose entry to integrations/catalog.json
* fix: remove unused Path import, add comment to empty except
* fix: validate descriptor root is dict, add shared infra to upgrade
- Add isinstance(self.data, dict) check at start of _validate() so
non-mapping YAML roots raise IntegrationDescriptorError
- Run _install_shared_infra() and ensure_executable_scripts() in
upgrade command to match install/switch behavior
* fix: address sixth round of PR review feedback
- Validate integration.id/name/version/description are strings
- Catch TypeError in pkg_version.Version() for non-string versions
- Swap validation order: check catalogs type before emptiness
- Isolate TestActiveCatalogs from user ~/.specify/ via monkeypatch
* fix: address seventh round of PR review feedback
- Update docs: version field uses PEP 440, not semver
- Harden search() against non-string author/name/description fields
- Validate requires.speckit_version is a non-empty string
- Validate command name/file are non-empty strings, file is safe relative path
- Handle stale symlinks in upgrade cleanup
- Document catalog configuration stack in README.md
* fix: validate script entries, remove destructive teardown from upgrade rollback
- Validate provides.scripts entries are non-empty strings with safe relative paths
- Remove teardown from upgrade rollback since setup overwrites in-place —
teardown would delete files that were working before the upgrade
* fix: use consistent resolved root for stale-file cleanup paths
* fix: validate redirect URL and reject drive-qualified paths
- Validate final URL after redirects with _validate_catalog_url()
- Reject paths with Path.drive or Path.anchor for Windows safety
- Update FakeResponse mocks with geturl() method
* fix: fix docstring backticks, assert file modification in upgrade tests
* docs: clarify directory naming convention for hyphenated integration keys
* fix: correct key type hint, isolate all catalog tests from env
- Fix key parameter type to str | None (defaults to None)
- Add HOME/USERPROFILE monkeypatch and clear SPECKIT_INTEGRATION_CATALOG_URL
in all TestCatalogFetch tests for full environment isolation
* fix: neutralize catalog table title, handle non-dict cache metadata
* fix: validate requires.tools entries in descriptor
* fix: show discovery-only status, clear metadata files in clear_cache
* fix: catch OSError/UnicodeError in cache read path
* refactor: reuse IntegrationManifest.uninstall for stale-file cleanup
* fix: normalize null tools to empty list in descriptor accessor
---------
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>
- Adds catalog-ci entry to catalog.community.json (between canon and ci-guard)
- Adds Catalog CI row to community extensions table in README.md
- Bumps top-level updated_at
* chore: bump version to 0.7.1
* chore: begin 0.7.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci: add windows-latest to test matrix
Add windows-latest to the pytest job OS matrix so tests run on both
Ubuntu and Windows for all Python versions.
Closes#2232
* test: skip bash-specific tests on Windows
Add sys.platform skip markers to all test classes and methods that
execute bash scripts via subprocess, so they are skipped on Windows
where bash is not available. Mixed classes with both bash and pwsh
tests have markers on individual bash methods only.
* test: fix 3 Windows-specific test failures
- test_manifest: use platform-appropriate absolute path (C:\ on Windows
vs /tmp on POSIX) since /tmp is not absolute on Windows
- test_extensions: add agent_scripts.ps entry and platform-conditional
assertions for codex skill fallback variant test
- test_timestamp_branches: use json.dumps() instead of f-string to
properly escape Windows backslash paths in feature.json
* test: extract requires_bash marker and fix PS test skip
Address PR review feedback:
- Define a reusable requires_bash marker in conftest.py and use it
across all 3 test files instead of repeating the skipif inline
- Move test_powershell_scanner_uses_long_tryparse_for_large_prefixes
into its own TestSequentialBranchPowerShell class so it is not
incorrectly skipped on Windows by the class-level bash marker
* test: use runtime bash check instead of platform check
Replace sys.platform == 'win32' with an actual bash invocation test
to handle environments where bash exists but is non-functional (e.g.,
WSL stub on Windows without an installed distro).
* test: reject WSL bash, accept only MSYS/MINGW on Windows
On Windows, verify uname -s reports MSYS, MINGW, or CYGWIN so the WSL
launcher (System32\bash.exe) is rejected — it cannot handle native
Windows paths used by test fixtures. Add SPECKIT_TEST_BASH=1 env var
escape hatch to force-enable bash tests in non-standard setups.
* ci: add comment explaining Windows bash test behavior
* test: early-reject WSL launcher, fix remaining f-string JSON
- Check resolved bash path for System32 before spawning any subprocess
to avoid WSL init prompts and timeout during test collection
- Convert remaining feature_json f-string writes to json.dumps() so
paths with backslashes produce valid JSON on Windows
* test: use bare 'bash' for detection to match test invocation
On Windows, subprocess.run(['bash', ...]) uses CreateProcess which
searches System32 before PATH — finding WSL bash even when
shutil.which('bash') returns Git-for-Windows. Probe with bare 'bash'
(same as test helpers) so the detection matches actual test behavior.
* fix: allow Claude to chain skills for hook execution (#2178)
- Set disable-model-invocation to false so Claude can invoke extension
skills (e.g. speckit-git-feature) from within workflow skills
- Inject dot-to-hyphen normalization note into Claude SKILL.md hook
sections so the model maps extension.yml command names to skill names
- Replace Unicode checkmark with ASCII [OK] in auto-commit scripts to
fix PowerShell encoding errors on Windows
- Move Claude-specific frontmatter injection to ClaudeIntegration via
post_process_skill_content() hook on SkillsIntegration, wired through
presets and extensions managers
- Add positive and negative tests for all changes
Fixes#2178
* refactor: address PR review feedback
- Preserve line-ending style (CRLF/LF) in _inject_hook_command_note
instead of always inserting \n, matching the convention used by other
injection helpers in the same module.
- Extract duplicated _post_process_skill() from extensions.py and
presets.py into a shared post_process_skill() function in agents.py.
Both modules now import and call the shared helper.
* fix: match full hook instruction line in regex
The regex in _inject_hook_command_note only matched lines ending
immediately after 'output the following', but the actual template
lines continue with 'based on its `optional` flag:'. Use [^\r\n]*
to capture the rest of the line before the EOL.
* refactor: use integration object directly for post_process_skill_content
Instead of a free function in agents.py that re-resolves the
integration by key, callers in extensions.py and presets.py now
resolve the integration once via get_integration() and call
integration.post_process_skill_content() directly. The base
identity method lives on SkillsIntegration.
* docs: warn about unofficial PyPI packages and recommend version verification (#1982)
Clarify that only packages from github/spec-kit are official, and add
`specify version` as a post-install verification step to help users
catch accidental installation of an unrelated package with the same name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): auto-correct legacy command names instead of hard-failing (#2017)
Community extensions that predate the strict naming requirement use two
common legacy formats ('speckit.command' and 'extension.command').
Instead of rejecting them outright, auto-correct to the required
'speckit.{extension}.{command}' pattern and emit a compatibility warning
so authors know they need to update their manifest. Names that cannot be
safely corrected (e.g. single-segment names) still raise ValidationError.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tests): isolate preset catalog search test from community catalog network calls
test_search_with_cached_data asserted exactly 2 results but was getting 4
because _get_merged_packs() queries the full built-in catalog stack
(default + community). The community catalog had no local cache and hit
the network, returning real presets. Writing a project-level
preset-catalogs.yml that pins the test to the default URL only makes
the count assertions deterministic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): extend auto-correction to aliases (#2017)
The upstream #1994 added alias validation in _collect_manifest_command_names,
which also rejected legacy 2-part alias names (e.g. 'speckit.verify').
Extend the same auto-correction logic from _validate() to cover aliases,
so both 'speckit.command' and 'extension.command' alias formats are
corrected to 'speckit.{ext_id}.{command}' with a compatibility warning
instead of hard-failing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): address PR review feedback (#2017)
- _try_correct_command_name: only correct 'X.Y' to 'speckit.ext_id.Y'
when X matches ext_id, preventing misleading warnings followed by
install failure due to namespace mismatch
- _validate: add aliases type/string guards matching _collect_manifest
_command_names defensive checks
- _validate: track command renames and rewrite any hook.*.command
references that pointed at a renamed command, emitting a warning
- test: fix test_command_name_autocorrect_no_speckit_prefix to use
ext_id matching the legacy namespace; add namespace-mismatch test
- test: replace redundant preset-catalogs.yml isolation with
monkeypatch.delenv("SPECKIT_PRESET_CATALOG_URL") so the env var
cannot bypass catalog restriction in CI environments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update docs/installation.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix(extensions): warn when hook command refs are silently canonicalized; fix grammar
- Hook rewrites (alias-form or rename-map) now always emit a warning so
extension authors know to update their manifests. Previously only
rename-map rewrites produced a warning; pure alias-form lifts were
silent.
- Pluralize "command/commands" in the uninstall confirmation message so
single-command extensions no longer print "1 commands".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): raise ValidationError for non-dict hook entries
Silently skipping non-dict hook entries left them in manifest.hooks,
causing HookExecutor.register_hooks() to crash with AttributeError
when it called hook_config.get() on a non-mapping value.
Also updates PR description to accurately reflect the implementation
(no separate _try_correct_alias_name helper; aliases use the same
_try_correct_command_name path).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): derive remove cmd_count from registry, fix wording
Previously cmd_count used len(ext_manifest.commands) which only counted
primary commands and missed aliases. The registry's registered_commands
already tracks every command name (primaries + aliases) per agent, so
max(len(v) for v in registered_commands.values()) gives the correct
total.
Also changes "from AI agent" → "across AI agents" since remove()
unregisters commands from all detected agents.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): distinguish missing vs empty registered_commands in remove prompt
Using get() without a default lets us tell apart:
- key missing (legacy registry entry) → fall back to manifest count
- key present but empty dict (installed with no agent dirs) → show 0
Previously the truthiness check `if registered_commands and ...` treated
both cases the same, so an empty dict fell back to len(manifest.commands)
and overcounted commands that would actually be removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): clarify removal prompt wording to 'per agent'
'across AI agents' implied a total count, but cmd_count uses max()
across agents (per-agent count). Using sum() would double-count since
users think in logical commands, not per-agent files. 'per agent'
accurately describes what the number represents.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): clarify cmd_count comment — per-agent max, not total
The comment said 'covers all agents' implying a total, but cmd_count uses
max() across agents (per-agent count). Updated comment to explain the
max() choice and why sum() would double-count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(extensions): add CLI tests for remove confirmation pluralization
Adds TestExtensionRemoveCLI with two CliRunner tests:
- singular: 1 registered command → '1 command per agent'
- plural: 2 registered commands → '2 commands per agent'
These prevent regressions on the cmd_count pluralization logic
and the 'per agent' wording introduced in this PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(agents): remove orphaned SKILL.md parent dirs on unregister
For SKILL.md-based agents (codex, kimi), each command lives in its own
subdirectory (e.g. .agents/skills/speckit-ext-cmd/SKILL.md). The previous
unregister_commands() only unlinked the file, leaving an empty parent dir.
Now attempts rmdir() on the parent when it differs from the agent commands
dir. OSError is silenced so non-empty dirs (e.g. user files) are safely left.
Adds test_unregister_skill_removes_parent_directory to cover this.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): drop alias pattern enforcement from _validate()
Aliases are intentionally free-form to preserve community extension
compatibility (e.g. 'speckit.verify' short aliases used by spec-kit-verify
and other existing extensions). This aligns _validate() with the intent of
upstream commit 4deb90f (fix: restore alias compatibility, #2110/#2125).
Only type and None-normalization checks remain for aliases. Pattern
enforcement continues for primary command names only.
Updated tests to verify free-form aliases pass through unchanged with
no warnings instead of being auto-corrected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(extensions): guard against non-dict command entries in _validate()
If provides.commands contains a non-mapping entry (e.g. an int or string),
'name' not in cmd raises TypeError instead of a user-facing ValidationError.
Added isinstance(cmd, dict) check at the top of the loop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: iamaeroplane <michal.bachorik@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* chore: deprecate --ai flag in favor of --integration on specify init
- Adds deprecation warning when --ai is used
- Shows equivalent --integration command replacement
- Handles generic integration with --commands-dir mapping
- Adds comprehensive test coverage for deprecation behavior
- Warning displays as prominent red panel above Next Steps
- --ai flag continues to function (non-breaking change)
Fixes#2169
* Address PR review feedback for issue #2169
- Use existing strip_ansi helper from conftest instead of duplicating ANSI escape pattern
- Properly escape ai_commands_dir with shlex.quote() to handle paths with spaces
- Add shlex import to support proper command-line argument escaping
* chore: bump version to 0.7.0
* chore: begin 0.7.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: add claude-ask-questions preset for AskUserQuestion rendering
Delivers the /speckit.clarify and /speckit.checklist AskUserQuestion
integration as a stackable preset under presets/claude-ask-questions/
instead of modifying core templates or ClaudeIntegration.
- presets/claude-ask-questions/preset.yml registers command overrides
for speckit.clarify and speckit.checklist following the same pattern
as the bundled lean preset.
- Override commands replace the Markdown-table question-rendering
blocks with AskUserQuestion instructions. Option | Description maps
to {label, description} for clarify; Option | Candidate | Why It
Matters maps to {label: Candidate, description: Why It Matters} for
checklist. Recommended option is placed first with a
"Recommended — <reasoning>" prefix; a final "Custom"/"Short" option
preserves the free-form ≤5-word escape hatch.
- Registered in presets/catalog.json as a bundled preset.
Core templates, ClaudeIntegration, and the existing test suite are
left untouched, so non-Claude agents and users who do not install
this preset see no behavior change.
Closes github/spec-kit#2181
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move claude-ask-questions preset to external repo
Per maintainer feedback on #2191, presets should be hosted on the
author's own GitHub repository and registered in catalog.community.json
rather than bundled in spec-kit. Removes the bundled preset directory
and its entry from the official catalog, and adds a community catalog
entry pointing at the external repository and release archive.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(catalog): sync claude-ask-questions description with upstream preset
* revert: keep presets/catalog.json updated_at unchanged
No entries in the official catalog changed in this PR, so the timestamp
bump was spurious. Addresses Copilot review feedback on #2191.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: bump version to 0.6.2
* chore: begin 0.6.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: Update catalog.community.json for preset-fiction-book-writing
* Add fiction-book-writing preset to community catalog
- Preset ID: fiction-book-writing
- Version: 1.3.0
- Author: Andreas Daumann
- Description: Spec-Driven Development for novel and long-form fiction. Replaces software engineering terminology with storytelling craft: specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports 8 POV modes, all major plot structure frameworks, 5 humanized-AI prose profiles, and exports to DOCX/EPUB/LaTeX via pandoc.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* doc: added fiction-book-writing preset link in README.md
* 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>
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
* feat(integrations): add YamlIntegration base class for YAML recipe agents
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* feat(integrations): add Goose integration subpackage with YAML recipe support
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* feat(integrations): register GooseIntegration in the integration registry
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* feat(agents): add YAML format support to CommandRegistrar for extension/preset commands
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* feat(scripts): add goose agent type to bash update-agent-context script
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* feat(scripts): add goose agent type to PowerShell update-agent-context script
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* docs(agents): add Goose to supported agents table and integration notes
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* docs(readme): add Goose to supported agents table
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* test(integrations): add YamlIntegrationTests base mixin for YAML agent testing
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* test(integrations): add Goose integration tests
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* test(consistency): add Goose consistency checks for config, registrar, and scripts
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* docs(agents): move Goose to YAML Format section in Command File Formats
Goose uses YAML recipes, not Markdown. Remove it from the Markdown Format
list and add a dedicated YAML Format subsection with a representative
recipe example showing prompt: | and {{args}} placeholders.
* refactor(agents): delegate render_yaml_command to YamlIntegration
Remove the duplicate header dict, yaml.safe_dump call, body indentation,
and _human_title logic from CommandRegistrar.render_yaml_command(). Delegate
to YamlIntegration._render_yaml() and _human_title() so YAML recipe output
stays consistent across the init-time generation and command-registration
code paths.
* fix(agents): guard alias output path against directory traversal
Validate that alias_file resolves within commands_dir before writing.
Uses the same resolve().relative_to() pattern already established in
extensions.py for ZIP path containment checks.
* docs(agents): add Goose to Multi-Agent Support comment list in update-agent-context.sh
* fix(agents): add goose to print_summary Usage line in bash context script
The print_summary() function listed all supported agents in its Usage
output but omitted goose, making it inconsistent with the header docs
and the error message in update_specific_agent().
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): add goose to Print-Summary Usage line in PowerShell context script
The Print-Summary function listed all supported agents in its Usage
output but omitted goose, making it inconsistent with the ValidateSet
and the header documentation.
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): normalize description and title types in YamlIntegration.setup()
YAML frontmatter can contain non-string types (null, list, int).
Add isinstance checks matching TomlIntegration._extract_description()
to ensure Goose recipes always receive valid string fields.
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): validate shared script exists before exec in Goose bash wrapper
Add Forge-style check that the shared update-agent-context.sh is
present and executable, producing a clear error instead of a cryptic
shell exec failure when the shared script is missing.
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): validate shared script exists before invoke in Goose PowerShell wrapper
Add Forge-style Test-Path check that the shared update-agent-context.ps1
exists, producing a clear error instead of a cryptic PowerShell failure
when the shared script is missing.
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): normalize title and description types in render_yaml_command()
Extension/preset frontmatter can contain non-string types. Add
isinstance checks matching the normalization in YamlIntegration.setup()
so both code paths produce valid Goose recipe fields.
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(agents): replace $ARGUMENTS with arg_placeholder in process_template()
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* test(agents): assert $ARGUMENTS absent from generated YAML recipes
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* test(agents): assert $ARGUMENTS absent from generated TOML commands
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* fix(tests): rewrite docstring to avoid embedded triple-quote in TOML test
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
---------
Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
* chore: bump version to 0.6.1
* chore: begin 0.6.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: add bundled lean preset with minimal workflow commands
Add a lean preset that overrides the 5 core workflow commands (specify,
plan, tasks, implement, constitution) with minimal prompts that produce
exactly one artifact each — no extension hooks, no scripts, no git
branching, no templates.
Bundled preset infrastructure:
- Add _locate_bundled_preset() mirroring _locate_bundled_extension()
- Update 'specify init --preset' to try bundled -> catalog fallback
- Update 'specify preset add' to try bundled -> catalog fallback
- Add bundled guard in download_pack() for presets without download URLs
- Add lean to presets/catalog.json with 'bundled: true' marker
- Add lean to pyproject.toml force-include for wheel packaging
- Align error messages with bundled extension error pattern
Tests: 15 new tests (TestLeanPreset + TestBundledPresetLocator)
* refactor: address review — clean up unused imports, strengthen test assertions
- Remove unused MagicMock import and cache_dir setup in download test
- Assert 'bundled' and 'reinstall' in CLI error output (not just exit code)
- Mock catalog in missing-locally test for deterministic bundled error path
- Fix test versions to satisfy updated speckit_version >=0.6.0 requirement
* refactor: address review — fix constitution paths, add REINSTALL_COMMAND to presets.py
- Fix constitution path to .specify/memory/constitution.md in plan, tasks,
implement commands (matching core command convention)
- Include REINSTALL_COMMAND in download_pack() bundled guard for consistent
recovery instructions across bundled extensions and presets
* refactor: address review — explicit feature_directory paths, ZIP cleanup in finally
- Prefix spec.md/plan.md/tasks.md with <feature_directory>/ in plan, tasks,
and implement commands so the agent doesn't operate on repo root by mistake
- Move ZIP unlink into finally block in init --preset path so cleanup runs
even when install_from_zip raises (matching preset_add pattern)
* refactor: address review — replace Unicode em dashes with ASCII, fix grammar
- Replace all Unicode em dashes with ASCII hyphens in preset.yml and
catalog.json to avoid decode errors on non-UTF-8 environments
- Fix grammar: 'store it in tasks.md' -> 'store them in tasks.md'
* refactor: address review - align task format between tasks and implement
- Remove undefined [P] marker from implement (lean uses sequential execution)
- Clarify checkbox update: 'change - [ ] to - [x]' instead of ambiguous '[X]'
- Simplify implement to execute tasks in order without parallel complexity
* refactor: address review - parse frontmatter instead of raw substring search
- Use CommandRegistrar.parse_frontmatter() to check for scripts/agent_scripts
keys in YAML frontmatter instead of brittle 'scripts:' substring search
* Add SpecTest extension to community catalog
Adds spec-kit-spectest: auto-generate test scaffolds from spec criteria.
4 commands:
- /speckit.test.generate — generate framework-native test scaffolds
- /speckit.test.coverage — map spec requirements to test coverage
- /speckit.test.gaps — find untested requirements with suggestions
- /speckit.test.plan — generate structured test plan documents
1 hook: after_implement (gap detection)
Bridges the spec-to-test gap in the SDD workflow.
* Update extensions/catalog.community.json
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fix spectest created_at/updated_at to use current timestamp per Copilot review
Set both to 2026-04-10T16:00:00Z instead of midnight.
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: bundled extensions should not have download URLs (#2151)
- Remove selftest from default catalog (not a published extension)
- Replace download_url with 'bundled: true' flag for git extension
- Add bundled check in extension add flow with clear error message
when bundled extension is missing from installed package
- Add bundled check in download_extension() with specific error
- Direct users to reinstall via uv with full GitHub URL
- Add 3 regression tests for bundled extension handling
* refactor: address review - move bundled check up-front, extract reinstall constant
- Move bundled check before download_url inspection in download_extension()
so bundled extensions can never be downloaded even with a URL present
- Extract REINSTALL_COMMAND constant to avoid duplicated install strings
* fix: allow bundled extensions with download_url to be updated
Bundled extensions should only be blocked from download when they have
no download_url. If a newer version is published to the catalog with a
URL, users should be able to install it to get bug fixes.
Add test for bundled-with-URL download path.
* chore: bump version to 0.6.0
* chore: begin 0.6.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add Bugfix Workflow community extension to catalog and README
Adds the spec-kit-bugfix extension (3 commands, 1 hook) that provides a
structured bugfix workflow — capture bugs, trace to spec artifacts, and
surgically patch specs without regenerating from scratch.
Addresses community request in issue #619 (25+ upvotes, maintainer-approved).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Bump catalog updated_at to 2026-04-09 to match new entry date
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Rewrite AGENTS.md for integration subpackage architecture
Replaces the old AGENT_CONFIG dict-based 7-step process with documentation
reflecting the integration subpackage architecture shipped in #1924.
Removed: Supported Agents table, old step-by-step guide referencing
AGENT_CONFIG/release scripts/case statements, Agent Categories lists,
Directory Conventions section, Important Design Decisions section.
Kept: About Spec Kit and Specify, Command File Formats, Argument Patterns,
Devcontainer section.
Added: Architecture overview, decision tree for base class selection,
configure/register/scripts/test/override steps with real code examples
from existing integrations (Windsurf, Gemini, Codex, Copilot).
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/71b25c53-7d0c-492a-9503-f40a437d5ece
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* Fix JSONC comment syntax in devcontainer example
Agent-Logs-Url: https://github.com/github/spec-kit/sessions/71b25c53-7d0c-492a-9503-f40a437d5ece
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* docs(AGENTS.md): address Copilot PR review comments
- Clarify that integrations are registered by _register_builtins() in
__init__.py, not self-registered at import time
- Scope the key-must-match-executable rule to CLI-based integrations
(requires_cli: True); IDE-based integrations use canonical identifiers
- Replace <commands_dir> placeholder in test snippet with a concrete
example path (.windsurf/workflows/)
- Document that hyphens in keys become underscores in test filenames
(e.g. cursor-agent -> test_integration_cursor_agent.py)
- Note that the argument placeholder is integration-specific
(registrar_config["args"]); add Forge's {{parameters}} as an example
- Apply consistency fixes to Required fields table, Key design rule
callout, and Common Pitfalls #1
* docs(AGENTS.md): clarify scripts path uses Python-safe package_dir not key
The scripts step previously referenced src/specify_cli/integrations/<key>/scripts/
but for hyphenated keys the actual directory is underscored (e.g. kiro-cli -> kiro_cli/).
Rename the placeholder to <package_dir> and add a note explaining:
- <package_dir> matches <key> for non-hyphenated keys
- <package_dir> uses underscores for hyphenated keys (e.g. kiro-cli -> kiro_cli/)
- IntegrationBase.key always retains the original hyphenated value
Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3054946896
* docs(AGENTS.md): use <key_with_underscores> in pytest example command
The pytest command previously used <key> as a placeholder, but test
filenames always use underscores even for hyphenated keys. This was
internally inconsistent since the preceding sentence already explained
the hyphen→underscore mapping. Switch to <key_with_underscores> to
match the actual filename on disk.
Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3054962863
* docs(AGENTS.md): use <package_dir> in step 2 subpackage path
The path src/specify_cli/integrations/<key>/__init__.py was inaccurate
for hyphenated keys (e.g. kiro-cli lives in kiro_cli/, not kiro-cli/).
Rename the placeholder to <package_dir>, define it inline (hyphens
become underscores), and note that IntegrationBase.key always retains
the original hyphenated value.
Addresses: https://github.com/github/spec-kit/pull/2119#discussion_r3058050583
* docs(AGENTS.md): qualify 'single source of truth' to Python metadata only
The registry is only authoritative for Python integration metadata.
Context-update dispatcher scripts (bash + PowerShell) still require
explicit per-agent cases and maintain their own supported-agent lists
until they are migrated to registry-based dispatch. Tighten the claim
to avoid misleading contributors into skipping the script updates.
Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083090261
* docs(AGENTS.md): mention ValidateSet update in PowerShell dispatcher step
The update-agent-context.ps1 script has a [ValidateSet(...)] on the
AgentType parameter. Without adding the new key to that list, the script
rejects the argument before reaching Update-SpecificAgent. Add this as
an explicit step alongside the switch case and Update-AllExistingAgents.
Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083217694
* fix(integrations): sort codebuddy before codex in _register_builtins()
Both the import list and the _register() call list had codex before
codebuddy, violating the alphabetical ordering that AGENTS.md documents.
Swap them so the file matches the documented convention.
Addresses: https://github.com/github/spec-kit/pull/2119#pullrequestreview-4083341590
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
* feat: add memorylint extension to community catalog
* chore: update speckit_version requirement to >=0.5.1 for memorylint extension
* docs: register memorylint extension in README and update requirements
* chore: bump version to 0.5.1
* chore: begin 0.5.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
typer <0.24.0 under-constrains its click dependency (click>=8.0.0),
allowing resolvers to pick click <8.2 which lacks __class_getitem__
on click.Choice. This causes 'TypeError: type Choice is not
subscriptable' at import time on any Python version.
Pin typer>=0.24.0 (which correctly requires click>=8.2.1) and
click>=8.2.1 to prevent incompatible combinations.
Fixes#2134
* fix(forge): use hyphen notation in frontmatter name field
- Changed injected name field from 'speckit.{command}' to 'speckit-{command}'
- Keeps standard filename format 'speckit.{command}.md'
- Aligns with Forge's command naming convention requirements
- All tests pass
* feat(forge): centralize name formatting to fix extension/preset command names
Address PR feedback by centralizing Forge command name formatting to ensure
consistent hyphenated names across both core template setup and
extension/preset command registration.
Changes:
- Add format_forge_command_name() utility function in forge integration
- Update ForgeIntegration._apply_forge_transformations() to use centralized formatter
- Add _format_name_for_agent() helper in CommandRegistrar to apply agent-specific formatting
- Update CommandRegistrar.register_commands() to format names for Forge (both primary commands and aliases)
- Add comprehensive test coverage for the formatter and registrar behavior
Impact:
- Extension commands installed for Forge now use 'name: speckit-my-extension-example'
instead of 'name: speckit.my-extension.example'
- Fixes ZSH/shell compatibility issues with dot notation in command names
- Maintains backward compatibility for all other agents (they continue using dot notation)
- Eliminates duplication between integration setup and registrar paths
Example transformation:
Before: name: speckit.jira.sync-status (breaks in ZSH/Forge)
After: name: speckit-jira-sync-status (works everywhere)
Fixes inconsistency where core templates used hyphens but extension/preset
commands preserved dots, breaking Forge's naming requirements.
* refactor(forge): move name formatting logic to integration module
Move _format_name_for_agent function logic into Forge integration's
registrar_config as a 'format_name' callback, improving separation of
concerns and keeping Forge-specific logic within its integration module.
Changes:
- Remove _format_name_for_agent() from agents.py (shared module)
- Add 'format_name' callback to Forge's registrar_config pointing to format_forge_command_name
- Update CommandRegistrar to use format_name callback when available
- Maintains same behavior: Forge commands use hyphenated names, others use dot notation
Benefits:
- Better encapsulation: Forge-specific logic lives in forge integration
- More extensible: Other integrations can provide custom formatters via registrar_config
- Cleaner separation: agents.py doesn't need to know about specific agent requirements
* fix(forge): make format_forge_command_name idempotent
Handle already-hyphenated names (speckit-foo) to prevent double-prefixing
(speckit-speckit-foo). The function now returns already-formatted names
unchanged, making it safe to call multiple times.
Changes:
- Add early return for names starting with 'speckit-'
- Update docstring to clarify accepted input formats
- Add examples showing idempotent behavior
- Add test coverage for idempotent behavior
Examples:
format_forge_command_name('speckit-plan') -> 'speckit-plan' (unchanged)
format_forge_command_name('speckit.plan') -> 'speckit-plan' (converted)
format_forge_command_name('plan') -> 'speckit-plan' (prefixed)
* test(forge): strengthen name field assertions and clarify comments
Improve test_name_field_uses_hyphenated_format to fail loudly when
the name field is missing instead of silently passing.
Changes:
- Add explicit assertion that name_match is not None before validating value
- Ensures test fails if regex doesn't match (e.g., frontmatter rendering changes)
- Clarify Claude comment: it doesn't use inject_name path but SKILL.md
frontmatter still includes hyphenated name via build_skill_frontmatter()
Before: Test would silently pass if 'name:' field was missing from frontmatter
After: Test explicitly asserts field presence before validating format
* docs(forge): clarify frontmatter name requirement and improve test isolation
Fix misleading docstring and improve test to properly validate that the
format_name callback is Forge-specific.
Changes to src/specify_cli/integrations/forge/__init__.py:
- Reword module docstring to clarify the requirement is specifically for
the frontmatter 'name' field value, not command files or invocation
- Before: 'Requires hyphenated command names ... instead of dot notation'
(implied dot notation unsupported overall)
- After: 'Uses a hyphenated frontmatter name value ... for shell compatibility'
(clarifies it's the frontmatter field, and Forge still supports dot filenames)
Changes to tests/integrations/test_integration_forge.py:
- Replace Claude with Windsurf in test_registrar_does_not_affect_other_agents
- Claude uses build_skill_frontmatter() which always includes hyphenated names,
so testing it didn't validate that format_name callback is Forge-only
- Windsurf is a standard markdown agent without inject_name
- Now asserts NO 'name:' field is present, proving format_name isn't invoked
- This properly validates the callback mechanism is isolated to Forge
* test(forge): use parse_frontmatter for precise YAML validation
Replace regex and string searches with CommandRegistrar.parse_frontmatter()
to validate only YAML frontmatter, not entire file content. Prevents false
positives if command body contains 'name:' lines.
Changes:
- test_forge_specific_transformations: Parse frontmatter dict instead of string search
- test_name_field_uses_hyphenated_format: Replace regex with frontmatter parsing
- test_registrar_formats_extension_command_names_for_forge: Use dict validation
- test_registrar_formats_alias_names_for_forge: Use dict validation
Benefits: More precise, robust against body content, better error messages,
consistent with existing codebase utilities.
---------
Co-authored-by: ericnoam <eric.rodriguez@leovegas.com>
* fix(bash): sed replacement escaping, BSD portability, dead cleanup code
Three bugs in update-agent-context.sh:
1. **sed escaping targets wrong side** (line 318-320): The escaping
function escapes regex pattern characters (`[`, `.`, `*`, `^`, `$`,
`+`, `{`, `}`, `|`) but these variables are used as sed
*replacement* strings, not patterns. Only `&` (insert matched text),
`\` (escape char), and `|` (our sed delimiter) are special in the
replacement context. Also adds escaping for `project_name` which
was used unescaped.
2. **BSD sed newline insertion fails on macOS** (line 364-366): Uses
bash variable expansion to insert a literal newline into a sed
replacement string. This works on GNU sed (Linux) but fails silently
on BSD sed (macOS). Replaced with portable awk approach that works
on both platforms.
3. **cleanup() removes non-existent files** (line 125-126): The
cleanup trap attempts `rm -f /tmp/agent_update_*_$$` and
`rm -f /tmp/manual_additions_$$` but the script never creates files
matching these patterns — all temp files use `mktemp`. The wildcard
with `$$` (PID) in /tmp could theoretically match unrelated files.
Fixes#154 (macOS sed failure)
Fixes#293 (sed expression errors)
Related: #338 (shellcheck findings)
* fix: restore forge case and revert copilot path change
Address PR review feedback:
- Restore forge) case in update_specific_agent since
src/specify_cli/integrations/forge/__init__.py still exists
- Revert COPILOT_FILE path from .github/agents/ back to .github/
to stay consistent with Python integration and tests
- Restore FORGE_FILE variable, comments, and usage strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract repeated sed escaping into _esc_sed helper
Address Gemini review feedback — the inline sed escaping pattern
appeared 7 times in create_new_agent_file(). Extract to a single
helper function for maintainability and readability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: restore combined AGENTS_FILE label in update_all_existing_agents
Gemini correctly identified that splitting AGENTS_FILE updates into
individual calls is redundant — _update_if_new deduplicates by
realpath, so only the first call logs. Restore the combined label
and add back missing Pi reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove pre-escaped && in JS/TS commands now that _esc_sed handles it
The old code manually pre-escaped & as \& in get_commands_for_language
because the broken escaping function didn't handle &. Now that _esc_sed
properly escapes replacement-side specials, the pre-escaping causes
double-escaping: && becomes \&\& in generated files.
Found by blind audit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: split awk && mv to let set -e catch awk failures
Under set -e, the left side of && does not trigger errexit on failure.
Split into two statements so awk failures are fatal instead of silent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: guard empty _CLEANUP_FILES array for Bash 3.2 compatibility
On Bash 3.2, the ${arr[@]+"${arr[@]}"} pattern expands to a single
empty string when the array is empty, causing rm to target .bak and
.tmp in the current directory. Use explicit length check instead,
which also avoids the word-splitting risk of unquoted expansion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Bo Bobson <bo@noneofyourbusiness.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: Git extension stage 2 — GIT_BRANCH_NAME override, --force for existing dirs, auto-install tests (#1940)
- Add GIT_BRANCH_NAME env var override to create-new-feature.sh/.ps1
for exact branch naming (bypasses all prefix/suffix generation)
- Fix --force flag for 'specify init <dir>' into existing directories
- Add TestGitExtensionAutoInstall tests (auto-install, --no-git skip,
commands registered)
- Add TestFeatureDirectoryResolution tests (env var, feature.json,
priority, branch fallback)
- Document GIT_BRANCH_NAME in speckit.git.feature.md and specify.md
* fix: remove unused Tuple import (ruff F401)
* fix: address Copilot review feedback (#2117)
- Fix timestamp regex ordering: check YYYYMMDD-HHMMSS before generic
numeric prefix in both bash and PowerShell
- Set BRANCH_SUFFIX in GIT_BRANCH_NAME override path so 244-byte
truncation logic works correctly
- Add 244-byte length check for GIT_BRANCH_NAME in PowerShell
- Use existing_items for non-empty dir warning with --force
- Skip git extension install if already installed (idempotent --force)
- Wrap PowerShell feature.json parsing in try/catch for malformed JSON
- Fix PS comment: 'prefix lookup' -> 'exact mapping via Get-FeatureDir'
- Remove non-functional SPECIFY_SPEC_DIRECTORY from specify.md template
* fix: address second round of Copilot review feedback (#2117)
- Guard shutil.rmtree on init failure: skip cleanup when --force merged
into a pre-existing directory (prevents data loss)
- Bash: error on GIT_BRANCH_NAME >244 bytes instead of broken truncation
- Fix malformed numbered list in specify.md (restore missing step 1)
- Add claude_skills.exists() assert before iterdir() in test
* fix: use UTF-8 byte count for 244-byte branch name limit (#2117)
- Bash: use LC_ALL=C wc -c for byte length instead of ${#VAR}
- PowerShell: use [System.Text.Encoding]::UTF8.GetByteCount() instead
of .Length (UTF-16 code units)
* fix: address third round of review feedback (#2117)
- Update --dry-run help text in bash and PowerShell (branch name only)
- Fix specify.md JSON example: use concrete path, not literal variable
- Add TestForceExistingDirectory tests (merge + error without --force)
- Add PowerShell Get-FeaturePathsEnv tests (env var + feature.json)
* fix: normalize relative paths and fix Test-HasGit compat (#2117)
- Bash common.sh: normalize SPECIFY_FEATURE_DIRECTORY and feature.json
relative paths to absolute under repo root
- PowerShell common.ps1: same normalization using IsPathRooted + Join-Path
- PowerShell create-new-feature.ps1: call Test-HasGit without -RepoRoot
for compatibility with core common.ps1 (no param) and git-common.ps1
(optional param with default)
* test: add GIT_BRANCH_NAME automated tests for bash and PowerShell (#2117)
- TestGitBranchNameOverrideBash: 5 tests (exact name, sequential prefix,
timestamp prefix, overlong rejection, dry-run)
- TestGitBranchNameOverridePowerShell: 4 tests (exact name, sequential
prefix, timestamp prefix, overlong rejection)
- Tests use extension scripts (not core) via new ext_git_repo and
ext_ps_git_repo fixtures
* fix: restore git init during specify init + review fixes (#2117)
- Restore is_git_repo() and init_git_repo() functions removed in stage 2
- specify init now runs git init AND installs git extension (not just
extension install alone)
- Add is_dir() guard for non-here path to prevent uncontrolled error
when target exists but is a file
- Add python3 JSON fallback in common.sh for multi-line feature.json
(grep pipeline fails on pretty-printed JSON without jq)
* fix: use init_git_repo error_msg in failure output (#2117)
* fix: ensure_executable_scripts also covers .specify/extensions/ (#2117)
Extension .sh scripts (e.g. create-new-feature.sh, initialize-repo.sh)
may lack execute bits after install. Scan both .specify/scripts/ and
.specify/extensions/ for permission fixing.
* fix: move chmod after extension install + sanitize error_msg (#2117)
- ensure_executable_scripts() now runs after git extension install so
extension .sh files get execute bits in the same init run
- Sanitize init_git_repo error_msg to single line (replace newlines,
truncate to 120 chars) to prevent garbled StepTracker output
* fix: use tracker.error for git init/extension failures (#2117)
Git init failure and extension install failure were reported as
tracker.complete (showing green) even on error. Now track a
git_has_error flag and call tracker.error when any step fails,
so the UI correctly reflects the failure state.
* fix: sanitize ext_err in git step tracker for consistent rendering (#2117)
Adds the spec-kit-branch-convention extension (3 commands, 1 hook) that
enables configurable branch and folder naming with built-in presets for
GitFlow, ticket-based, date-based, and custom patterns.
Addresses community request in issue #407 (39+ upvotes).
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove PR/issue number references throughout
- Shorten summary table cells
- Break version wall-of-text into shorter per-version paragraphs
- Trim blog post summaries to key insights
- Condense community tools and industry coverage sections
- Merge competitive landscape subsections
Relax alias validation in _collect_manifest_command_names() to only
enforce the 3-part speckit.{ext}.{cmd} pattern on primary command
names. Aliases retain type and duplicate checking but are otherwise
free-form, restoring pre-#1994 behavior.
This unblocks community extensions (e.g. spec-kit-verify) that use
2-part aliases like 'speckit.verify'.
Fixes#2110
* Add Spec Refine community extension to catalog and README
Adds the spec-kit-refine extension (4 commands, 2 hooks) that enables
iterative specification refinement — update specs in-place, propagate
changes to plan and tasks, diff impact, and track sync status.
Addresses community request in issue #1191 (101+ upvotes).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix alphabetical ordering of S-entries in Community Extensions table
Reorders Ship Release, Spec Critique, Spec Refine, Spec Sync, Staff Review,
and Superpowers Bridge into correct alphabetical order per publishing guide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add Table of Contents to generated markdown documents (#1970)
* fix: address Copilot review - clarify TOC placement wording
* fix: include TOC sections in structure templates
* fix: include TOC in structure templates and fix tasks TOC placement wording
* fix: correct TOC anchors to match headings with mandatory suffix
* fix: include all ##-level headings in tasks-template TOC
* fix: add missing TOC entries in tasks-template, remove leading blank line in
* fix: move TOC after metadata block and include all ## headings in tasks-template
* fix: use plain text for dynamic phase entries in tasks-template TOC
* fix: remove hardcoded anchor links from template TOCs, use plain text exemplars
* fix: remove HTML comments from template TOCs
* fix: add missing Parallel Example heading to tasks-template TOC
* revert: remove all core template changes, pivot to preset approach
* feat: deliver TOC navigation as a preset (closes#1970)
Pivots from core template changes to a preset approach per reviewer
request. Adds presets/toc-navigation/ with 3 template overrides and
3 command overrides that add Table of Contents sections to generated
spec.md, plan.md, and tasks.md documents.
Addresses all 8 impact concerns from review:
- Templates use anchor links (not plain text) matching command instructions
- All 12 tasks-template headings accounted for (dynamic phases as plain text)
- spec-template anchors include -mandatory suffix
- TOC placed after Note paragraph in plan-template
- Self-reference exclusion explicit in all commands
- Clarify stale TOC instruction in specify command
- Implement misparse warning in tasks command
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: publish toc-navigation preset to community catalog (#1970)
Move preset to standalone repository per maintainer guidance:
https://github.com/Quratulain-bilal/spec-kit-preset-toc-navigation
- Remove presets/toc-navigation/ from core repo
- Add toc-navigation entry to catalog.community.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add toc-navigation preset to main README community presets table
Adds Table of Contents Navigation entry (alphabetically between Pirate
Speak and VS Code Ask Questions) to the community presets table in
README.md as requested by maintainer.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: prevent ambiguous TOML closing quotes when body ends with `"` (#2113)
_render_toml_string placed the closing `"""` inline with content, so
a body ending with `"` produced `""""` (four consecutive quotes).
While technically valid TOML 1.0, this breaks stricter parsers such as
Gemini CLI v0.27.2.
Insert a newline before the closing delimiter when the body ends with a
quote character. Same treatment for the single-quote (`'''`) fallback.
Adds both a positive test (body ending with `"` must not produce
`""""`) and a negative test (safe bodies keep the inline delimiter).
* fix: use line-ending backslash instead of newline for TOML closing delimiters
Address PR review feedback:
- Replace sep=newline with TOML line-ending backslash so the parsed
value does not gain a trailing newline when body ends with a quote.
- For literal string (''') fallback, skip to escaped basic string when
value ends with single quote instead of inserting a newline.
- Make test body multiline so it exercises the """ rendering path,
and assert no trailing newline in parsed value.
* test: cover escaped basic-string fallback when body has triple-quotes and ends with single-quote
Addresses review feedback from PR #2115: adds test for the branch
where the body contains '"""' and ends with "'", which forces
_render_toml_string() through the escaped basic-string fallback
instead of the '''...''' literal-string path (since '''' would
produce the same ambiguous-closing-delimiter problem).
* feat: add git extension with hooks on all core commands
- Create extensions/git/ with 5 commands: initialize, feature,
validate, remote, commit
- 18 hooks covering before/after for all 9 core commands
- Scripts: create-new-feature, initialize-repo, auto-commit,
git-common (bash + powershell)
- Configurable: branch_numbering, init_commit_message,
per-command auto-commit with custom messages
- Add hooks to analyze, checklist, clarify, constitution,
taskstoissues command templates
- Allow hooks-only extensions (no commands required)
- Bundle extension in wheel via pyproject.toml force-include
- Resolve bundled extensions locally before catalog lookup
- Remove planned-but-unimplemented before/after_commit hook refs
- Update extension docs (API ref, dev guide, user guide)
- 37 new tests covering manifest, install, all scripts (bash+pwsh),
config reading, graceful degradation
Stage 1: opt-in via 'specify extension add git'. No auto-install,
no changes to specify.md or core git init code.
Refs: #841, #1382, #1066, #1791, #1191
* fix: set git identity env vars in extension tests for CI runners
* fix: address PR review comments
- Fix commands property KeyError for hooks-only extensions
- Fix has_git() operator precedence in git-common.sh
- Align default commit message to '[Spec Kit] Initial commit' across
config-template, extension.yml defaults, and both init scripts
- Update README to reflect all 5 commands and 18 hooks
* fix: address second round of PR review comments
- Add type validation for provides.commands (must be list) and hooks
(must be dict) in manifest _validate()
- Tighten malformed timestamp detection in git-common.sh to catch
7-digit dates without trailing slug (e.g. 2026031-143022)
- Pass REPO_ROOT to has_git/Test-HasGit in create-new-feature scripts
- Fix initialize command docs: surface errors on git failures, only
skip when git is not installed
- Fix commit command docs: 'skips with a warning' not 'silently'
- Add tests for commands:null and hooks:list rejection
* fix: address third round of PR review comments
- Remove scripts frontmatter from command files (CommandRegistrar
rewrites ../../scripts/ to .specify/scripts/ which points at core
scripts, not extension scripts)
- Update speckit.git.commit command to derive event name from hook
context rather than using a static example
- Clarify that hook argument passthrough works via AI agent context
(the agent carries conversation state including user's original
feature description)
* fix: address fourth round of PR review comments
- Validate extension_id against ^[a-z0-9-]+$ in _locate_bundled_extension
to prevent path traversal (security fix)
- Move defaults under config.defaults in extension.yml to match
ConfigManager._get_extension_defaults() schema
- Ship git-config.yml in extension directory so it's copied during
install (provides.config template isn't materialized by ExtensionManager)
- Condition handling in hook templates: intentionally matches existing
pattern from specify/plan/tasks/implement templates (not a new issue)
* fix: add --allow-empty to git commit in initialize-repo scripts
Ensures git init succeeds even on empty repos where nothing has been
staged yet.
* fix: resolve display names to bundled extensions before catalog download
When 'specify extension add "Git Branching Workflow"' is used with a
display name instead of the ID, the catalog resolver now runs first to
map the name to an ID, then checks bundled extensions again with the
resolved ID before falling back to network download.
Also noted: EXECUTE_COMMAND_INVOCATION and condition handling match the
existing pattern in specify/plan/tasks/implement templates (pre-existing,
not introduced by this PR).
* fix: handle before_/after_ prefixes in auto-commit message derivation
- Strip both before_ and after_ prefixes when deriving command name
(fixes misleading 'Auto-commit after before_plan' messages)
- Include phase (before/after) in default commit messages
- Clarify README config example is an override, not default behavior
* fix: use portable grep -qw for word boundary in create-new-feature.sh
BSD grep (macOS) doesn't support \b as a word boundary. Replace with
grep -qw which is POSIX-portable.
* fix: validate hook values, numeric --number, and PS warning routing
- Validate each hook value is a dict with a 'command' field during
manifest _validate() (prevents crash at install time)
- Validate --number is a non-negative integer in bash create-new-feature
(clear error instead of cryptic shell arithmetic failure)
- Route PowerShell no-git warning to stderr in JSON mode so stdout
stays valid JSON
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Two test assertions in test_timestamp_branches.py used the regex
`\d{3}` (exactly 3 digits) instead of `\d{3,}` (3 or more digits).
While the underlying shell scripts already handle spec numbers ≥ 1000
correctly — printf "%03d" and PowerShell '{0:000}' both expand naturally
beyond 3 digits, and all detection regexes use {3,} — the overly-strict
test assertions would fail with a misleading error if a fixture ever
contained 1000+ spec directories.
Documentation in README.md, spec-driven.md, and the CLI --branch-numbering
help text implied that sequential spec numbers are always 3 digits, which
could lead users to believe a hard limit of 999 exists.
Changes:
- tests/test_timestamp_branches.py: change two \d{3} assertions to \d{3,}
- src/specify_cli/__init__.py: clarify help text to show numbers expand past 999
- README.md: update --branch-numbering docs to note numbers expand beyond 3 digits
- spec-driven.md: update feature numbering description to include 4-digit example
Fixes#2093
Co-authored-by: alex-zwingli <alex-zwingli@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>