* 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>