mirror of
https://github.com/github/spec-kit
synced 2026-06-21 13:51:39 +00:00
v0.9.2
1061 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c3194c543b | chore: bump version to 0.9.2 v0.9.2 | ||
|
|
9768b1eb88 | Update agent parity governance preset catalog entry (#2777) | ||
|
|
c9c02ae790 |
fix: resolve GitHub release asset API URL for private repo extension downloads (#2792)
* fix: resolve GitHub release asset API URL for private repo downloads For private or SSO-protected GitHub repos, browser release download URLs redirect to HTML/SSO instead of the ZIP asset. This commit resolves the asset via the GitHub REST API and downloads with Accept: application/octet-stream, falling back to the original URL if the API call fails. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: support direct GitHub REST release asset URLs in extension downloads When a catalog download_url is already a GitHub REST release asset URL (https://api.github.com/repos/<owner>/<repo>/releases/assets/<id>), skip the release metadata lookup and download directly with Accept: application/octet-stream. This complements the browser URL resolution from the previous commit, covering catalogs that reference the REST API directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
d79a514b30 |
fix: remove unsupported mode: frontmatter from Copilot skills mode (fixes #2799) (#2819)
VS Code Copilot Agent Skills do not support the `mode:` frontmatter field. The generated SKILL.md files included `mode: speckit.<stem>` injected by CopilotIntegration.post_process_skill_content(), which had no effect in VS Code and could cause confusion. Simplify post_process_skill_content to delegate directly to _CopilotSkillsHelper without injecting mode:. Update tests to assert mode: is absent from generated skill frontmatter. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
ee17b04784 |
refactor(integrations): co-locate integration commands in integrations/ domain dir (PR-5/8) (#2720)
* refactor(integrations): co-locate integration commands in integrations/ domain dir
- Remove commands/ stubs (handlers will live in domain dirs)
- Move all integration CLI handlers out of __init__.py into integrations/
- Split into focused modules under integrations/:
_helpers.py (340 lines) — domain helpers
_install_commands.py (306 lines) — install / uninstall
_migrate_commands.py (487 lines) — switch / upgrade
_query_commands.py (442 lines) — list / use / search / info / catalog
_commands.py (34 lines) — app objects + register()
- __init__.py reduced by ~1400 lines; integration block replaced with register() call
- Fix patch paths in tests to new module locations
* fix(integrations): restore original integration list output in refactor
Preserve the CLI Required column, post-table default/installed summary,
and no-installed guidance that were dropped during the no-behavior-change
refactor of integration list into _query_commands.py.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix(integrations): restore _clear/_update_init_options public imports
The refactor that split integration commands moved
_clear_init_options_for_integration and _update_init_options_for_integration
into integrations/_helpers.py, but tests still import them from the top-level
specify_cli package, causing ImportError. Re-export them with explicit aliases
at the end of __init__.py to preserve the public import surface.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
||
|
|
a1b8de68bc |
Update Product Forge extension to v1.6.0 (#2820)
* Update Product Forge extension to v1.6.0 Update product-forge extension submitted by @VaiYav:\n- extensions/catalog.community.json (version, download_url, description, provides, updated_at)\n- docs/community/extensions.md community extensions table\n\nCloses #2800 * Fix Product Forge typography in catalog/docs Replace ASCII '->' with Unicode '→' in Product Forge descriptions to match existing catalog/docs typography. |
||
|
|
7bab0568c5 |
feat(workflows): add continue_on_error step field for non-halting failures (#2663)
* feat(workflows): add continue_on_error step field
Adds an optional `continue_on_error: bool` field on every step.
When set to `true` and the step fails, the engine records the
result (`exit_code`, `stderr` on `steps.<id>.output` plus `status`
as a sibling key on `steps.<id>`) and continues to the next sibling
step instead of halting the run. Downstream `if`, `switch`, or
`gate` steps can then branch on
`{{ steps.<id>.output.exit_code }}` to route the recovery path.
Engine details
--------------
`WorkflowEngine._execute_steps` now consults the step config when a
step returns `StepStatus.FAILED`:
- Gate aborts (`output.aborted`) always halt the run — operator
decisions take precedence over the flag.
- Otherwise, if `continue_on_error` is the literal `True`, log a
`step_continue_on_error` event and proceed to the next sibling.
The runtime check uses identity comparison (`is True`) rather
than truthiness, so truthy non-bool values like the string
`"true"` cannot silently change run semantics even if a caller
bypasses `validate_workflow()`.
- Otherwise, behave as before: log `step_failed`, set
`RunStatus.FAILED`, and return.
Validation
----------
`_validate_steps` rejects non-bool values for `continue_on_error`.
Coerced strings like `"true"` are not accepted so authoring
mistakes surface at validation time rather than silently changing
run semantics.
Tests
-----
`TestContinueOnError` in `tests/test_workflows.py` (8 tests):
- `test_undeclared_failure_halts_run` — default halt behaviour.
- `test_declared_and_fired_continues_run` — flag + fail → continue.
- `test_declared_but_step_succeeded_is_noop` — flag + success → no-op.
- `test_if_branch_routes_around_failure` — end-to-end recovery.
- `test_gate_abort_still_halts_with_continue_on_error` — abort
always halts.
- `test_validation_rejects_non_bool_continue_on_error` — `"true"`
rejected at validation.
- `test_validation_accepts_bool_continue_on_error` — `true`/`false`
pass cleanly.
- `test_engine_ignores_truthy_non_bool_continue_on_error` —
defense-in-depth: engine ignores string `"true"` even when
validation is bypassed.
Rebased onto current upstream/main (post #2664 merge); the new
`TestContinueOnError` class sits immediately after upstream's
`TestContextRunId` so the two feature suites coexist cleanly.
Closes #2591.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(workflows): restore runtime context section, clarify gate prompt
Two Copilot findings on
|
||
|
|
7c558ab241 |
chore: add .editorconfig for consistent code formatting (#2366)
Signed-off-by: Srikanth Patchava <srpatcha@users.noreply.github.com> Co-authored-by: Srikanth Patchava <srpatcha@users.noreply.github.com> |
||
|
|
39921ddd3b |
fix(shared-infra): record skipped files in speckit.manifest.json (#2483)
* fix(shared-infra): record skipped files in speckit.manifest.json
`install_shared_infra` skipped files that already existed on disk
when `force=False`, but the skip branches in both the scripts loop
and the templates loop only appended to `skipped_files` without
calling `manifest.record_existing`. So when the function ran with a
fresh manifest against an already-populated `.specify/` tree (e.g.
after the manifest was deleted, corrupted, or extracted out of band),
every file went down the skip path, `planned_copies` /
`planned_templates` stayed empty, and `manifest.save()` wrote an
empty `files` field — leaving the integration believing nothing was
installed.
Record every skipped file in the manifest, but only when it is not
already tracked. This preserves the original hash for files that
were previously recorded so `check_modified()` (used by
`integration use` to decide whether a user has customized a
template) keeps working correctly.
Add `TestSpeckitManifestRecordsSkippedFiles` in
`tests/integrations/test_integration_claude.py` covering both the
fresh-skip path and the recover-after-lost-manifest path.
Fixes #2107
* fix(shared-infra): guard manifest.record_existing against non-file dst
Address Copilot review feedback on PR #2483. The previous fix called
``manifest.record_existing(rel_skip)`` from the skip branch of both
loops in ``install_shared_infra``, which would crash with
``IsADirectoryError`` (or another ``OSError``) if a directory or other
non-regular-file happened to exist at the expected destination path —
since ``record_existing`` opens the file to compute its SHA-256.
Three coordinated fixes:
1. ``IntegrationManifest.record_existing`` now validates its
precondition: it raises ``ValueError`` if the path is a symlink or
is not a regular file. The docstring already promised "an
already-existing file"; this enforces it. The symlink check runs on
the un-resolved path because ``_validate_rel_path`` calls
``resolve()``, which would silently follow the symlink. Mirrors the
existing ``_ensure_safe_manifest_destination`` precedent in the
same module.
2. In ``install_shared_infra``'s scripts and templates skip branches,
guard the ``record_existing`` call with ``dst.is_file()`` and wrap
it in ``try/except (OSError, ValueError)``. A directory collision,
permission error, or TOCTOU race no longer aborts the whole
install — the user gets a per-path warning, the path still
surfaces in ``skipped_files``, and the rest of the install
continues.
3. ``_read_manifest_files`` in the regression test no longer falls
back to ``data.get("_files")`` (Copilot's low-confidence finding):
the silent fallback could mask a schema regression where the
public ``files`` key is renamed. It now asserts ``"files" in data``
and that the value is a dict.
Add two regression tests in ``TestSpeckitManifestRecordsSkippedFiles``
covering the directory-at-destination edge case for both the scripts
loop and the templates loop. Both verify (a) install does not crash,
(b) the non-file path is not recorded in the manifest, and (c) the
path still surfaces in the user-visible warning.
The "shared infrastructure file(s)" warning text is changed to
"path(s)" so it remains accurate when non-file entries appear in the
list.
Refs #2107
* fix(manifest): lexical pre-check for record_existing + add error-case tests
Address Copilot review (2026-05-11, review id 4266902103):
1. `record_existing` was calling `(self.project_root / rel).is_symlink()`
BEFORE validating containment. For absolute paths or paths containing
`..`, this performed a filesystem stat outside the project root before
`_validate_rel_path()` raised. Add a cheap lexical pre-check that
delegates to `_validate_rel_path()` for the canonical error messages,
so the symlink stat only ever runs on paths that are already lexically
inside the project root.
2. Add focused unit tests in `tests/integrations/test_manifest.py` for
the symlink and non-regular-file error paths, including:
- symlink target rejection
- dangling symlink rejection (caught by the symlink guard before
the is_file check)
- directory path rejection (is_file == False)
- missing-path rejection (is_file == False)
- absolute-path lexical pre-check
The Copilot reviewer noted these guards had no focused coverage in
`test_manifest.py`, only via the `test_integration_claude.py`
regression test.
3. The third Copilot finding (repeated `dict(self._files)` copies via
`manifest.files` in the skip branches) is already resolved on this
branch by using `prior_hashes` — the function-scope snapshot taken at
the top of `install_shared_infra` — for the membership check, instead
of `manifest.files`.
AI disclosure: drafted with assistance from Claude (Opus 4.7).
* fix(manifest): track recovered files separately + symlink-ancestor + canonical-path guards
Address Copilot review id 4309888722 (2026-05-18) on PR #2483:
1. Recovery semantics (shared_infra.py:371, 412) — install_shared_infra
now passes ``recovered=True`` when re-recording a skipped existing
file. This flag funnels into a new ``recovered_files`` array in the
manifest JSON, so a future ``refresh_managed`` run can distinguish
"hash I produced" from "hash I observed on a file that may be a user
customization" and avoid silent overwrite without ``--refresh-shared-infra``.
Schema is purely additive: ``files: dict[str, str]`` is unchanged; the
new ``recovered_files: list[str]`` is omitted when empty.
2. Symlinked ancestor (manifest.py:172) — ``record_existing`` now walks
every component of the rel path and rejects any symlinked ancestor,
not just a symlinked leaf. Catches ``linked_dir/file.txt`` where
``linked_dir`` is a symlink, which previously slipped past the leaf-only
``is_symlink()`` check and was resolved through by ``_validate_rel_path``.
Mirrors the component-walk pattern in ``_ensure_safe_manifest_directory``.
3. Misleading "escapes project root" message (manifest.py:168) — paths
like ``dir/../file.txt`` normalize inside the project, so the old
message lied about what was wrong. New message: "Manifest paths must
be canonical; '..' segments are not allowed". Still rejects (canonical
keys are required so ``check_modified``/``uninstall`` cannot key the
same file under two paths).
Tests: 7 new test methods across TestManifestRecoveredFiles and
TestRecordExistingNewGuards covering all 4 Copilot findings. Full suite
passes locally.
🤖 AI disclosure: drafted with assistance from Claude (Opus 4.7).
* fix(manifest): normalize is_recovered input through _validate_rel_path
Address Copilot review comment id 4309888722 round-5 (2026-05-21) on PR #2483:
``is_recovered()`` previously checked ``self._recovered_files`` membership
with bare ``Path(rel).as_posix()``, while ``record_existing()`` stores keys
via ``_validate_rel_path(rel, root).relative_to(root).as_posix()``. The two
normalizations disagreed on absolute paths and paths that escape the
project root — ``is_recovered`` would silently return False for inputs that
``record_existing`` would have refused entirely.
The fix routes ``is_recovered`` through the same ``_validate_rel_path``
pipeline; ``ValueError`` from the validator is caught and converted to
False so query semantics stay exception-free (Python ``__contains__``
convention).
Tests: 2 new methods in ``TestManifestRecoveredFiles``:
- ``test_is_recovered_absolute_path_returns_false``
- ``test_is_recovered_escaping_path_returns_false``
🤖 AI disclosure: drafted with assistance from Claude (Opus 4.7).
* fix(manifest): clear recovered marker on managed re-record + reject '..' in is_recovered
Address Copilot Round-7 review comments on PR #2483:
1. record_existing(recovered=False) and record_file now BOTH discard the
path from _recovered_files. The marker is meant to flag "we observed
this file but cannot vouch it's a managed baseline" — once the same
path is re-recorded as managed (either explicitly or by writing fresh
bytes), the marker is stale and must clear so refresh_managed and
future is_recovered queries return the truthful answer.
2. is_recovered now applies the same canonical-key guard as record_existing
(rejects absolute paths and '..' segments lexically before delegating
to _validate_rel_path). Such paths can never be stored keys, so the
query correctly returns False without depending on _validate_rel_path
semantics that diverged from record_existing's stricter contract.
record_file docstring updated to mention the side-effect on recovered
markers.
Tests: 3 new methods in TestManifestRecoveredFiles covering
record_existing(false) clearing, record_file clearing, and is_recovered
dotdot rejection.
* test(manifest): update is_recovered comments to reflect Round-7 lexical guard
Round 8 — addresses Copilot review comment on tests/integrations/test_manifest.py:362.
After Round-7 (
|
||
|
|
d82eed859c |
chore: release 0.9.1, begin 0.9.2.dev0 development (#2818)
* chore: bump version to 0.9.1 * chore: begin 0.9.2.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
442a581358 |
fix(cli): pin UTF-8 encoding on init-options and .extensionignore I/O (#2686)
* fix(cli): pin UTF-8 encoding on init-options and .extensionignore I/O
``Path.read_text`` / ``Path.write_text`` default to the system locale
codec, which is cp1252 / gb2312 / cp932 on Windows. Two user-facing
file paths in spec-kit were calling them without an explicit
``encoding=`` argument:
- ``src/specify_cli/__init__.py:400,412`` —
``save_init_options`` / ``load_init_options`` for
``.specify/init-options.json``. A peer machine with a different
default locale (or a UTF-8 Unix CI runner reading a file written on
a cp1252 Windows host) cannot decode the file, raising
``UnicodeDecodeError``. ``UnicodeDecodeError`` is a subclass of
``ValueError`` — not ``OSError`` / ``json.JSONDecodeError`` — so
the existing fall-back ``except`` tuple in ``load_init_options``
also misses it and the error propagates raw to the CLI.
- ``src/specify_cli/extensions.py:764`` — ``.extensionignore``
pattern reader. The very next line already normalises
backslashes "so Windows-authored files work", proving the codebase
expects Windows authors to write this file. Multibyte UTF-8
patterns (Chinese filenames, accented directory names) silently
mojibake when the host locale is not UTF-8, so the patterns fail
to match and unintended files are shipped with the extension.
The sibling integration-catalog reader at
``src/specify_cli/integrations/catalog.py:150,156,193,202,374``
already pins ``encoding="utf-8"`` everywhere. PR #2280 fixed the
symmetric PowerShell-template BOM bug. This change brings the two
remaining drifted paths in line with that precedent.
Regression tests:
- ``tests/test_presets.py::TestInitOptions`` — parametrized non-ASCII
round-trip (CJK, Latin-1, Greek, emoji) plus a corrupted-file case
that asserts the existing "fall back to {}" contract still holds
when a peer file contains bytes invalid as UTF-8.
- ``tests/test_extensions.py::TestExtensionIgnore`` — Japanese
(``ドキュメント/``) and Latin-1 (``café/``) ignore patterns
correctly exclude their directories during install.
* fix(cli): wrap .extensionignore decode error and tighten UTF-8 contract
Addresses Copilot review feedback on this PR.
Three issues, three fixes:
1. ``save_init_options`` now writes JSON with ``ensure_ascii=False``.
Without that flag, ``json.dumps`` emits ASCII-only ``\uXXXX``
escapes, which means the ``encoding="utf-8"`` pin on the
surrounding ``Path.write_text`` makes no observable difference for
any value we currently write. Flipping ``ensure_ascii`` makes the
non-ASCII bytes hit the file directly, so the encoding pin becomes
the thing that decides between cp1252 garbage and clean UTF-8 on
Windows. The comment above the call now describes the real reason
instead of the previously-misleading rationale Copilot flagged.
2. ``test_save_load_round_trip_preserves_non_ascii`` was a no-op under
the old ``ensure_ascii=True`` writer (Copilot's second comment).
Added ``test_save_writes_real_utf8_bytes`` that asserts the on-disk
bytes contain the UTF-8 encoding of ``café`` (``0xC3 0xA9``), not
its JSON escape form ``é``. Removing either
``ensure_ascii=False`` or ``encoding="utf-8"`` from the writer now
breaks this test — the contract is pinned.
3. ``.extensionignore`` reader wraps ``UnicodeDecodeError`` as
``ValidationError`` with a pointer to the offending byte
(Copilot's third comment). Mirrors
``ExtensionManifest._load_yaml``'s existing handler for
``extension.yml``. Adds
``test_extensionignore_invalid_utf8_raises_validation_error``
asserting installation aborts with the wrapped error instead of a
raw Python traceback.
|
||
|
|
ed10b32014 |
docs: list Hermes in supported integrations table (#2768)
The Hermes Agent integration ships in the CLI (src/specify_cli/integrations/hermes/) and is registered in the catalog, but the supported-agents table in the integrations reference omitted it. Add the row so the docs match the shipped integration. |
||
|
|
14da893e4f |
fix(copilot): resolve active spec template (#2765)
Co-authored-by: root <kinsonnee@gmail.com> |
||
|
|
39925ac084 |
fix: add missing agent-context extension entries to Cline _expected_files (#2797)
TestClineIntegration._expected_files() overrides the base-class version but was not updated when the bundled agent-context extension files were added to test_integration_base_markdown.py, causing test_complete_file_inventory_sh and test_complete_file_inventory_ps to fail. Fixes #2796 |
||
|
|
866424385c |
Add spec-kit-linear extension to community catalog (#2795)
* Add spec-kit-linear extension to community catalog Add linear extension submitted by @ashbrener to:\n- extensions/catalog.community.json\n- docs/community/extensions.md\n\nCloses #2778 * Address PR review feedback for spec-kit-linear entry - Use Unicode arrow (→) in catalog/docs description\n- Move docs row to alphabetical Spec section * Address follow-up review naming/order feedback - Use human-friendly display name: Linear Integration\n- Move docs row to alphabetical L section |
||
|
|
44aac9f6e4 |
feat: add native Cline integration (#2508)
* test: strip ansi to make asserts work * feat: add native Cline integration |
||
|
|
4230685e26 | Update workflow-preset community catalog entry (#2756) | ||
|
|
258dd8e380 |
chore: release 0.9.0, begin 0.9.1.dev0 development (#2794)
* chore: bump version to 0.9.0 * chore: begin 0.9.1.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
122a794d83 |
Add RAG Azure Builder extension to community catalog (#2793)
Add rag-azure-builder extension submitted by @Sertxito to:\n- extensions/catalog.community.json\n- docs/community/extensions.md\n\nCloses #2665 |
||
|
|
c5865ef444 |
chore: recompile workflow lock files (#2774)
Regenerate lock files via `gh aw compile` to sync frontmatter hashes with their source .md files. Closes #2773 |
||
|
|
a042c785f5 |
Add Multi-Sites Spec Kit extension to community catalog (#2791)
* Add Multi-Sites Spec Kit extension to community catalog Add multi-sites extension submitted by @teeyo to:\n- extensions/catalog.community.json\n- docs/community/extensions.md\n\nCloses #2770 * Improve Multi-Sites extension description readability * Revert Multi-Sites listing description wording |
||
|
|
ac0c17c28f |
Update Product Spec Extension to v0.8.3 (#2790)
Update product extension submitted by @d0whc3r: - extensions/catalog.community.json (version, download_url, metadata) Closes #2767 |
||
|
|
5d6d199aaa |
Publish May 2026 Newsletter (#2787)
* docs: add May 2026 newsletter Publish the May 2026 newsletter documenting project milestones including: - Crossing 100K GitHub stars and top-100 GitHub project status - 100+ community extensions in catalog - Fourteen releases (v0.8.4–v0.8.17) - Multi-agent install support and constitution governance features - Open Source Friday livestream and media coverage across 25+ languages - Industry analyst recognition * 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 Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
089feca75f |
fix: move URL install confirmation prompt before spinner (#2783) (#2784)
* fix: move URL install confirmation prompt before spinner (#2783) The typer.confirm() prompt inside console.status() was overwritten by Rich's spinner animation, making extension add --from <url> appear hung. Move URL validation and the default-deny confirmation prompt before the spinner block so the user can see and respond to the [y/N] prompt. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: guard prompt with not dev, escape from_url in Rich markup Address PR review feedback: - Gate URL confirmation prompt on 'not dev' so --dev + --from does not show a confusing prompt for a URL path that will be ignored. - Escape from_url with rich.markup.escape() in both the warning panel and the download message to prevent markup injection via crafted URLs. * fix: remove unused import, reuse safe_url, add regression tests Address second round of PR review: - Remove unused urllib.request import from URL install path - Remove redundant re-import of rich.markup.escape; reuse safe_url computed before the spinner for download and error messages - Add test_add_from_url_prompts_before_spinner: asserts typer.confirm fires before console.status spinner to prevent #2783 regression - Add test_add_from_url_cancel_exits_cleanly: asserts declining the prompt exits with code 0 and prints Cancelled --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
3617cd9c02 |
Update Reqnroll BDD extension to v1.1.0 (#2775)
Update reqnroll-bdd extension submitted by @stenyin: - extensions/catalog.community.json (version, download_url, updated_at) Closes #2764 |
||
|
|
50da3a0f77 |
Extract agent context updates into bundled agent-context extension (#2546)
* Initial plan * Extract agent context updates into bundled agent-context extension * Potential fix for pull request finding 'Unused import' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Potential fix for pull request finding 'Unused import' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: address review comments on agent-context extension - bash: parse init-options.json with a single python3 invocation instead of three separate read_json_field calls, for parity with the PowerShell ConvertFrom-Json approach and to avoid divergent error semantics - bash: use parameter expansion to strip PROJECT_ROOT prefix from plan path instead of sed interpolation, avoiding special-character fragility - powershell: limit Get-ChildItem to -Depth 1 so plan.md discovery matches the bash glob specs/*/plan.md (one level deep) — fixes cross-platform inconsistency with nested plan.md files - powershell: replace Substring+Length relative-path with [System.IO.Path]::GetRelativePath for robustness across case/PSDrive differences - __init__.py: move agent-context extension install to after save_init_options so init-options.json is present when hooks run - __init__.py: seed context_markers in init-options only when context_file is truthy; avoids noise for integrations without a context file - integrations/base.py: narrow blanket except Exception in _resolve_context_markers to ImportError / (OSError, ValueError) so unexpected bugs surface instead of being silently swallowed * fix: gate context_markers in _update_init_options_for_integration on context_file Apply the same gating logic used during `specify init`: only write context_markers to init-options.json when the integration actually has a context_file set. When switching to an integration without a context file the stale markers are removed, keeping the two init paths consistent. * fix: move context_file/context_markers from init-options.json to agent-context extension config * Potential fix for pull request finding 'Unused global variable' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: clarify local import comment in agents.py * Fix remaining agent-context review findings * Fix follow-up agent-context review issues * Address review feedback: narrow except, improve PyYAML messaging, surface config-written note * Fix double-space in PyYAML install hint message * 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> * 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> * Address latest agent-context review feedback * Harden bash config parse output handling * Clarify ImportError-only fallback comment * Apply review feedback: drop dead try/except, guard ext-config creation, explicit ConvertFrom-Yaml check * Remove redundant $Options = $null in PS1 catch block * Add constitution directives, deprecation warning, agent-context auto-install, and init flow fix - Add constitution-loading directive to specify, clarify, tasks, checklist, taskstoissues commands - Add deprecation warning (v0.12.0) in upsert_context_section() - Auto-install agent-context extension during specify init - Move context_file from init-options.json to agent-context extension config - Add tests: deprecation warning, corrupt config, constitution directives - Update file inventories across all integration tests * Address review: fix init ordering, test coverage, and hermes inventory - Move agent-context extension install after init-options.json is saved so skill registration can read ai_skills + integration key - Write extension config after install (avoids template overwriting context_file) - Fix test_defaults_when_markers_field_missing to truly test missing markers key - Update hermes tests to allow extension-installed agent-context skill * Address review: chmod ordering, preserve markers, PS1 Python check, YAML key order - Move ensure_executable_scripts after agent-context extension install so extension scripts get execute bits set - Use preserve_markers=True on reinit to keep user-customized markers - Add Python 3 version check in PowerShell fallback (matching bash behavior) - Add sort_keys=False to yaml.safe_dump for stable config output * Address review: path traversal guards and docstring fix - Reject absolute paths and '..' segments in context_file in both bash and PowerShell scripts to prevent writes outside the project root - Fix docstring in _update_init_options_for_integration to accurately describe marker preservation behavior * Address review: strict enabled check, docstring, segment-level path traversal - Use 'is not False' for enabled check so only literal False disables - Update upsert_context_section docstring to mention disabled-extension return - Fix path traversal guards to check actual path segments, not substrings (allows filenames like 'notes..md' while rejecting '../' traversal) * Address review: UnicodeError handling, missing extension warning - Add UnicodeError to exception tuples in _load_agent_context_config and _resolve_context_markers so garbled UTF-8 config files fall back to defaults - Emit error (with reinstall command) instead of silent skip when bundled agent-context extension is not found during init * Address review: bash backslash traversal guard, wheel packaging - Reject backslash separators and Windows drive-letter paths in bash context_file validation (prevents traversal on Git-Bash/Windows) - Add extensions/agent-context to pyproject.toml force-include so the bundled extension is included in wheel builds * Address review: write extension config before init-options.json - Reorder writes in _update_init_options_for_integration so the agent-context extension config is updated first; if it fails, init-options.json remains consistent with the previous state --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> |
||
|
|
cd8a39f50e |
chore(deps): bump actions/setup-dotnet from 5.2.0 to 5.3.0 (#2755)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.2.0 to 5.3.0. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7...9a946fdbd5fb07b82b2f5a4466058b876ab72bb2) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-version: 5.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
e53cb2c143 |
chore: release 0.8.18, begin 0.8.19.dev0 development (#2766)
* chore: bump version to 0.8.18 * chore: begin 0.8.19.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
cc3d828227 |
Add support for SPECKIT_WORKFLOW_RUN_ID override (#2742)
* Initial plan * feat: support SPECKIT_WORKFLOW_RUN_ID override * docs: clarify run_id env var precedence wording --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
b4e5a1c3be |
feat: support SPECKIT_INTEGRATION_<KEY>_EXECUTABLE env var (#2743)
* Initial plan * feat: support SPECKIT_INTEGRATION_<KEY>_EXECUTABLE env var override Adds `IntegrationBase._resolve_executable()` which reads `SPECKIT_INTEGRATION_<KEY>_EXECUTABLE` (hyphens→underscores, uppercased) and falls back to `self.key` when unset or whitespace-only. All concrete `build_exec_args()` implementations now call `self._resolve_executable()` instead of hard-coding `self.key` or `"agy"` as the first argv token: - MarkdownIntegration, TomlIntegration, SkillsIntegration (base classes) - CodexIntegration, DevinIntegration, OpencodeIntegration, HermesIntegration, AgyIntegration - CopilotIntegration (overrides `_resolve_executable()` to fall back to the platform-specific `_copilot_executable()` default; also updates `dispatch_command()` to use `_resolve_executable()`) Tests added to tests/integrations/test_extra_args.py covering: - default (unset) falls back to key - env var replaces first argv token - whitespace-only env var is a no-op - key hyphen→underscore normalisation - cross-integration scoping (wrong key ignored) - all override integrations (Codex, Devin, Opencode, Copilot) - Copilot dispatch_command path - EXECUTABLE and EXTRA_ARGS can be set simultaneously See issue #2596." * fix: complete docstring sentence in _resolve_executable * test: generalize extra-args test comments for override coverage * Fix stale Codex executable comment --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
11bd31935f |
chore(deps): bump github/gh-aw-actions from 0.74.8 to 0.77.0 (#2754)
Bumps [github/gh-aw-actions](https://github.com/github/gh-aw-actions) from 0.74.8 to 0.77.0. - [Release notes](https://github.com/github/gh-aw-actions/releases) - [Changelog](https://github.com/github/gh-aw-actions/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/gh-aw-actions/compare/efa55847f72aadb03490d955263ff911bf758700...b11be78086764c43fa463398aed7ffdcf40549c1) --- updated-dependencies: - dependency-name: github/gh-aw-actions dependency-version: 0.77.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
a130b7e8d1 |
chore(deps): bump github/codeql-action from 4.35.5 to 4.36.0 (#2753)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.5 to 4.36.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/9e0d7b8d25671d64c341c19c0152d693099fb5ba...7211b7c8077ea37d8641b6271f6a365a22a5fbfa) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
5372dcbdea |
fix: disable no-op issue reporting for catalog submission workflows (#2748)
Add noop: report-as-issue: false to safe-outputs frontmatter in both add-community-extension and add-community-preset workflows to prevent them from posting noise comments to the [aw] No-Op Runs tracking issue. Closes #2747 |
||
|
|
b48b22379e |
Add confirmation prompt for URL-based extension installs (#2745)
Display a yellow warning panel and default-deny [y/N] prompt when installing extensions via --from <url>, since this bypasses the catalog trust boundary. |
||
|
|
3f096ffcfc |
fix: restrict community submission workflows to labeled event only (#2741)
Both add-community-preset and add-community-extension workflows previously triggered on issues opened, edited, and labeled events. This caused them to fire on every new issue and post noisy bot comments explaining the issue wasn't a submission (see #2739). Changes: - Narrow trigger from [opened, edited, labeled] to [labeled] only - Update prompt instructions to stop silently on non-matching issues instead of posting a comment |
||
|
|
f50839a928 |
feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags (#2596)
* feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags Read a per-integration env var (SPECIFY_<KEY>_EXTRA_ARGS) inside `SkillsIntegration.build_exec_args`, `MarkdownIntegration.build_exec_args`, and `TomlIntegration.build_exec_args` and append the parsed flags to the spawned agent's argv, gated per integration key. Operators can now opt into extra CLI flags (e.g. `SPECIFY_CLAUDE_EXTRA_ARGS=--dangerously-skip-permissions`) without modifying any SKILL or workflow YAML. Useful in CI / non-interactive contexts where the spawned `<agent> -p ...` would otherwise hang on an internal permission or input prompt invisible to the parent `specify workflow run` process. Key normalization: `kiro-cli` → `SPECIFY_KIRO_CLI_EXTRA_ARGS` (hyphen replaced with underscore, then uppercased). Default (env var unset or whitespace-only) is byte-identical to previous behaviour. Extra args are inserted between `-p prompt` and the model / output-format flags so they cannot clobber canonical Spec Kit args. Implementation: a single helper `IntegrationBase._apply_extra_args_env_var` encapsulates the env-var read + shlex parsing; each of the three concrete `build_exec_args` implementations calls it. Closes #2595 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(integrations): wire SPECIFY_<KEY>_EXTRA_ARGS into Codex/Devin/Opencode/Copilot Four integrations override `build_exec_args` and were silently ignoring the env-var hook introduced in the previous commit: - CodexIntegration (`codex exec ...`) - DevinIntegration (`devin -p ...`) - OpencodeIntegration (`opencode run ...`) - CopilotIntegration (`copilot -p ...`) Each now calls `self._apply_extra_args_env_var(args)` between the base argv and the canonical Spec Kit flags (matching the placement in `MarkdownIntegration`, `TomlIntegration`, and `SkillsIntegration`), so operator-injected flags cannot clobber `--model` / `--output-format` / `--json`. Adds 4 parameterized override-integration tests locking the wiring per agent. Also cleans up an inline `__import__("os").environ` in the fixture to a top-of-file `import os`. Drive-by typing fix: guard `self.registrar_config.get(...)` in `CopilotIntegration.add_commands` against the `None` case, matching the pattern already used in `base.py` for the same access. Addresses Copilot review on #2596. * fix(integrations): apply Opencode extra-args before prompt-derived --command When the Opencode prompt starts with `/`, `build_exec_args` injects `--command <X>` derived from the prompt. The previous placement of `self._apply_extra_args_env_var(args)` appended operator-injected args AFTER that `--command`, so a user setting `SPECIFY_OPENCODE_EXTRA_ARGS="--command override"` could redirect the command under typical last-wins repeated-flag CLI semantics. Move the hook to immediately after `args = [self.key, "run"]`, before the prompt-parsing block. The operator's `--command override` (if any) now precedes the Spec Kit-derived `--command speckit`, so the canonical choice wins. Adds `test_opencode_extra_args_cannot_clobber_prompt_derived_command` locking the ordering. Also corrects the module docstring to describe the hook as living in `IntegrationBase` (not `SkillsIntegration`) and to acknowledge that this file covers Codex/Devin/Opencode/Copilot in addition to SkillsIntegration stubs. Addresses Copilot review on #2596. * fix(integrations): honour SPECIFY_COPILOT_EXTRA_ARGS in dispatch_command `CopilotIntegration` is the only integration that overrides `dispatch_command` — it builds `cli_args` inline rather than going through `build_exec_args`. The previous commit wired `_apply_extra_args_env_var` into `build_exec_args` but workflow execution calls `dispatch_command`, so `SPECIFY_COPILOT_EXTRA_ARGS` was silently ignored at runtime. Add the hook in `dispatch_command` immediately after `cli_args = ["copilot", "-p", prompt]`, mirroring the placement in `build_exec_args` (between `-p prompt` and the canonical `--agent` / `--yolo` / `--model` / `--output-format` flags). `IntegrationBase.dispatch_command` already delegates to `build_exec_args`, so Codex, Devin, and Opencode continue to honour their respective env vars through inheritance — no further changes needed for them. Adds two end-to-end tests that monkeypatch `subprocess.run` and assert the env-var args reach the executed argv: - `test_copilot_dispatch_command_includes_extra_args` locks the bypass fix on the overridden path. - `test_codex_dispatch_command_includes_extra_args` locks the inherited-base-dispatch path for the other three integrations. Addresses Copilot review on #2596. * refactor(integrations): rename env var to SPECIFY_INTEGRATION_<KEY>_EXTRA_ARGS Per maintainer request: scope the operator-injected env var to the integration subsystem by prepending `INTEGRATION_` to the key segment, so it does not collide with other Spec Kit env-var namespaces. Renames everywhere it appears: - Helper `IntegrationBase._apply_extra_args_env_var` env_name format and docstring (`base.py`). - Inline comment in `CopilotIntegration.dispatch_command`. - All `monkeypatch.setenv(...)` calls, docstrings, and the autouse fixture's scope filter in `tests/integrations/test_extra_args.py`. No behaviour change beyond the variable name. Default (env var unset) still byte-identical to before this PR. Addresses review on #2596. * fix(integrations): raise actionable error on malformed EXTRA_ARGS quoting Wrap `shlex.split` in `_apply_extra_args_env_var` so an unmatched quote in `SPECIFY_INTEGRATION_<KEY>_EXTRA_ARGS` surfaces a clear `ValueError` naming the offending env var and showing the invalid value, instead of crashing workflow dispatch with a bare shlex traceback. Adds a new test locking the actionable error path. Addresses Copilot review feedback on #2596. * test(integrations): use `_copilot_executable()` in Copilot extra-args test `test_copilot_integration_honours_extra_args` hardcoded `"copilot"` in the expected argv, but `CopilotIntegration.build_exec_args` calls `_copilot_executable()` which returns `"copilot.cmd"` on Windows (`os.name == "nt"`). The test passed on Linux/macOS and failed on all three Windows-latest matrix entries. Resolve by importing `_copilot_executable` alongside `CopilotIntegration` and using it as the first expected argv token. The companion `test_copilot_dispatch_command_includes_extra_args` already uses `index()` lookups rather than full-argv equality so it was unaffected. * fix(integrations): couple Codex executable to self.key + cover base classes Two Copilot findings on the latest pass: 1. `CodexIntegration.build_exec_args` hardcoded the executable name as the literal `"codex"` while the env-var lookup derives from `self.key`. The two should stay coupled (matching Devin/Opencode, which both use `self.key` already). Replace the literal with `self.key` so the argv and env-var scoping cannot drift. 2. `tests/integrations/test_extra_args.py` covered the `SkillsIntegration` mechanism (via stubs near the top) and the four `build_exec_args` overrides (Codex/Devin/Opencode/Copilot) end-to-end, but did not exercise the `MarkdownIntegration` or `TomlIntegration` base implementations directly. Add bare `_MarkdownAgentStub` and `_TomlAgentStub` test stubs and a test each — the most common integration pattern (Amp, Auggie, Generic, Gemini, Tabnine, …) inherits without overriding, so the base wiring is now locked. Full suite: 3011 passed (was 3009), 40 skipped, no regressions. Addresses Copilot review feedback on #2596. * fix(integrations): rename env var to SPECKIT_INTEGRATION_<KEY>_EXTRA_ARGS Renames the env-var hook prefix from `SPECIFY_INTEGRATION_*` to `SPECKIT_INTEGRATION_*` to match the established codebase convention for integration-subsystem env vars (`SPECKIT_INTEGRATION_CATALOG_URL` in `integrations/catalog.py`, `SPECKIT_COPILOT_ALLOW_ALL_TOOLS` in `integrations/copilot/__init__.py`). The `SPECIFY_*` prefix is reserved for user-facing feature-resolution variables (`SPECIFY_FEATURE`, `SPECIFY_FEATURE_DIRECTORY`); reusing it for integration-subsystem scoping would introduce a second integration namespace under a different prefix, confusing operators who already set `SPECKIT_INTEGRATION_CATALOG_URL`. Also reverts the unrelated defensive `arg_placeholder` / `registrar_config is None` guard in `CopilotIntegration.setup_skills_mode` — it was a drive-by pyright cleanup mixed into this PR. Every concrete integration sets `registrar_config` so the guard never fires in practice; the typing issue belongs in a focused follow-up rather than this env-var-hook PR. Updates everywhere the old prefix appeared: - `IntegrationBase._apply_extra_args_env_var` helper + docstring - `CopilotIntegration.dispatch_command` inline comment - All `monkeypatch.setenv(...)` calls in `tests/integrations/test_extra_args.py` - The autouse fixture scope filter - Test module docstring Full suite: 3011 passed, 40 skipped, no regressions. Addresses Copilot review feedback on #2596. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ae96f97035 |
chore: release 0.8.17, begin 0.8.18.dev0 development (#2737)
* chore: bump version to 0.8.17 * chore: begin 0.8.18.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
ad62357015 |
docs: consolidate Community sections in README (#2736)
* docs: consolidate Community sections in README Replace four separate Community sections (Extensions, Presets, Walkthroughs, Friends) with a single consolidated section containing a bullet list, one shared disclaimer, and both publishing guide links. * fix: broken community anchor links and missing Hermes hook note injection - Update README.md and extensions/README.md to point community extension links to the docs site instead of removed section anchor - Add post_process_skill_content() call in Hermes setup() so hook command notes are injected into generated skills - Add Hermes test override for test_hook_sections_explain_dotted_command_conversion with Path.home() monkeypatch |
||
|
|
57a518a583 |
Fix shared script command hints for integration separators (#2627)
* fix shared script command refs for integration separators * Fix integration use shared infra refresh hint * Clarify shared infrastructure force wording --------- Co-authored-by: root <1647273252@qq.com> Co-authored-by: root <kinsonnee@gmail.com> |
||
|
|
db81a719a4 | docs: update security-governance preset to v0.4.0 (#2703) | ||
|
|
6d25d869b3 |
feat(agy): enhance Google Antigravity CLI integration (#2689)
* feat(agy): enhance Google Antigravity CLI integration - Set requires_cli=True and install_url for CLI tool detection - Implement build_exec_args() for non-interactive execution via agy --print - Add dot-to-hyphen hook command note injection in generated SKILL.md files * fix(agy): add --ignore-agent-tools to TestAgyAutoPromote tests Tests verify file layout and setup warnings, not CLI presence. agy requires_cli=True causes CI failures when agy is not installed. |
||
|
|
9307093d8a |
Fix --dev extension agent symlinks (#2554)
* Fix dev extension agent symlinks * Address dev symlink review feedback * fix: handle dev symlink relpath failures * fix: fall back when dev cache writes fail * test: cover dev symlink fallback without privileges |
||
|
|
5a678c552e |
Share skills hook note post-processing (#2679)
* fix(integrations): share skills hook note post-processing * fix(integrations): tighten skill post-processing Apply skill content post-processing before the initial write, use an exact hook-note sentinel for idempotence, and route Copilot skill post-processing through the shared helper before adding mode frontmatter. * Make hook note injection per instruction * Deduplicate Codex hook note processing --------- Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com> Co-authored-by: Puneet Dixit <puneetdixit200@users.noreply.github.com> |
||
|
|
5a50b75adb |
feat: add Hermes Agent integration (with review fixes) (#2651)
* feat: add Hermes Agent integration * feat: add Hermes Agent integration * feat: add Hermes Agent integration * feat: add Hermes Agent integration (with review fixes) - Full SkillsIntegration subclass with dual install strategy (project-local .hermes/skills/ + global ~/.hermes/skills/) - CLI fix: integration_uninstall now calls integration.teardown() instead of manifest.uninstall() directly, allowing custom cleanup - Fix Copilot review issues: - Docstring now reflects both -Q (quiet) and -q (query) flags - Empty command guard prevents passing empty skill names - Add catalog entry for hermes in integrations/catalog.json Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> * feat: write Hermes skills directly to global ~/.hermes/skills/ Hermes loads skills from the global ~/.hermes/skills/ directory, not from project-local paths. The old dual-install strategy copied SKILL.md files to both locations — project-local (for manifest tracking) and global (for Hermes discovery). This change removes the project-local copies entirely: - setup() writes directly to ~/.hermes/skills/speckit-*/SKILL.md - An empty .hermes/skills/ marker directory is created in the project so extension commands (e.g. git) can detect Hermes as an active integration via register_commands_for_all_agents() - teardown() cleans both the global speckit-* dirs and the local marker - import yaml moved to local import inside setup() Tests updated: Hermes-specific tests now assert global skill location, and shared SkillsIntegrationTests that assumed project-local files are overridden with Hermes-appropriate assertions. Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> * fix: address Copilot review feedback on Hermes integration Addresses all 6 review comments from copilot-pull-request-reviewer: 1. Hard-fail on missing integration key → fall back to manifest.uninstall() with a warning instead of raising an error. Allows users to always remove stale integration files even when the integration class is missing from the registry. 2. HOME isolation in tests → every test that calls setup() or CliRunner now monkeypatches Path.home() to a temp directory, keeping the test suite hermetic and non-destructive. 3. HermesIntegration.teardown() now delegates to manifest.uninstall() for project-local tracked files (scripts, manifest), merging results with global cleanup. 4. Global skills cleanup gated behind force=True to avoid destroying speckit-* skills shared across multiple Spec Kit projects when running 'specify integration uninstall hermes' without --force. 5. Line 160 isolation (CLI test test_complete_file_inventory_sh). 6. Line 258 isolation (Path.home assertion in test_ai_hermes_without_ai_skills_auto_promotes). * fix: address second Copilot review round — 6 remaining observations - Move to module scope (was inside per-template loop) - Add safety checks in setup() matching standard - Fix docstrings: global skills always removed on uninstall (standard) - Fix removal tracking: only report after successful rmtree - Override shared test_modified_file_survives_uninstall with Hermes-appropriate behaviour (global skills always removed, no hash tracking) - Update PR description to match implementation (global-only skills + marker) * fix: add first-class global/home-based agent dir support in CommandRegistrar Resolves Copilot HIGH concern (discussion_r3312194525): HermesIntegration.registrar_config.dir was '.hermes/skills' (project- relative), but skills live in ~/.hermes/skills/ (global). Extensions and presets registering commands for the 'hermes' agent via CommandRegistrar would write to the project-local marker directory instead of the real global skills directory, making those commands invisible to Hermes. Fix consists of three parts: 1. CommandRegistrar._resolve_agent_dir now supports '~/'-prefixed and absolute paths in agent_config['dir']. Relative paths still resolve against project_root as before — zero change for existing agents (Claude, Codex, Gemini, etc.). 2. HermesIntegration.registrar_config.dir changed from '.hermes/skills' to '~/.hermes/skills', so extensions/presets write directly to the global directory Hermes searches at runtime. 3. Two inline project_root / agent_config['dir'] calls in the extension update backup/restore paths (src/specify_cli/__init__.py) now delegate to _resolve_agent_dir, giving them the same global-dir support plus the legacy_dir fallback they were missing (improvement for all agents). Test side-effect: test_update_failure_rolls_back_registry_hooks_and_commands was constructing verification paths with project_dir / '~/.hermes/skills' (literal tilde) — fixed to use _resolve_agent_dir and monkeypatch Path.home() so Hermes' global dir doesn't leak into the real filesystem. * fix: address remaining 3 Copilot review observations (round 3) - teardown docstring: clarify marker removal is conditional (if empty) - test_pre_existing_skills_not_removed: now actually calls teardown() to verify foreign skills survive uninstall (was only running setup) - integration_switch Phase 1: replaced old_manifest.uninstall() + remove_context_section() with current_integration.teardown(), matching the pattern already used in integration_uninstall. This ensures custom teardown logic (e.g. Hermes global skills cleanup) runs during switches. * fix: address Copilot round 4 — home-relative dir resolution + project-local detection 1. _resolve_agent_dir(): expand ~/... via Path.home() + slice instead of expanduser(), so tests that monkeypatch Path.home() properly isolate the home directory (Copilot r3312731595, r3312731729) 2. Add detect_dir field to registrar_config: Hermes declares detect_dir='.hermes/skills' (project-local marker). CommandRegistrar checks detect_dir before resolving the output dir, preventing global dirs like ~/.hermes/skills from causing false detection in every project (Copilot r3312731682) 3. test_update_failure_rolls_back: no additional changes needed — the _resolve_agent_dir fix makes the existing Path.home() monkeypatch effective, so ~/.hermes/skills is not found in the fake home and Hermes is properly skipped. Tests: 2236 passed (2009 integration + 195 extension + 32 hermes) --------- Co-authored-by: Zhaoxiaoguang001 <3357983213@qq.com> Co-authored-by: majordave <majordave@users.noreply.github.com> |
||
|
|
0a8f31ef18 |
Update Superpowers Implementation Bridge to v0.7.0 (#2732)
* Update Superpowers Implementation Bridge to v0.7.0 Update speckit-superpowers-bridge extension submitted by @lihan3238: - extensions/catalog.community.json (version 0.5.0 → 0.7.0, download_url → stable-alias) Closes #2731 * 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> |
||
|
|
cec63d34e3 |
chore: release 0.8.16, begin 0.8.17.dev0 development (#2729)
* chore: bump version to 0.8.16 * chore: begin 0.8.17.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
b58a121771 |
docs: update landing page stats and branch naming convention (#2727)
* docs: update landing page stats and branch naming convention - Update community extensions: 91 → 105 - Update extension authors: 50+ → 60+ - Update presets: 18 → 22 - Update GitHub stars: 96K+ → 106K+ - Add last-updated date to landing page - Clarify branch naming convention for PR-only changes * 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> |
||
|
|
c6afe4cde1 |
feat(workflows): expose {{ context.run_id }} template variable (#2664)
* feat(workflows): expose `{{ context.run_id }}` template variable
Closes #2590.
Surfaces the engine-assigned run id (the same 8-character hex
string Spec Kit prints as `Run ID:` at the end of
`workflow run`) as a workflow template variable so YAML
authors can reference it from shell `run:`, command
`input.args:`, switch `expression:`, and any other field that
already evaluates `{{ ... }}` templates.
### Why
The run id is the natural join key between a Spec Kit workflow
run and downstream artifacts, telemetry, or per-run scratch
state. Today the operator sees it in stdout but workflows
themselves cannot reference it — there was no way to stamp a
log line, name a scratch directory, or tag an artifact with
the same id Spec Kit assigned.
The three motivating use cases from the issue:
1. Telemetry / observability — stamp logs and events with the
run id so external systems can join workflow runs to
downstream artifacts.
2. Per-run scratch / isolation — interactive operator commands
that need their own state directory under
`/tmp/run-<id>/`.
3. Run-id in artifact metadata — stable join key from artifact
back to the producing run.
### Implementation
`StepContext.run_id` is already populated by `WorkflowEngine`
in both `execute()` and `resume()`. The only gap was the
template namespace builder.
`_build_namespace` (in `workflows/expressions.py`) now adds a
`context` key alongside the existing `inputs`, `steps`,
`item`, and `fan_in` namespaces:
```python
ns["context"] = {"run_id": run_id}
```
The value is always present (even outside a run) and falls
back to an empty string when no run is active. Workflows
referencing `{{ context.run_id }}` therefore never error — a
hard requirement from the issue's acceptance criteria for
dry-run, validation, and ad-hoc evaluator usage.
### Default behaviour preserved
Workflows that do not reference `{{ context.run_id }}` are
byte-equivalent to before this change. The `context`
namespace is added unconditionally to keep template
resolution branch-free, but its presence has no observable
effect when nothing references it.
### Tests
`TestExpressions` (unit-level) gains three tests:
- `test_context_run_id_resolves` — direct lookup against a
`StepContext(run_id=...)`.
- `test_context_run_id_defaults_to_empty_when_unset` —
graceful default outside a run context.
- `test_context_run_id_string_interpolation` — mixed
template (e.g. `"RUN_ID={{ context.run_id }}"`).
`TestContextRunId` (end-to-end) covers the three step types
the acceptance criteria called out:
- `test_shell_run_resolves_run_id` — `run:` field
substitution, verified via captured stdout.
- `test_command_input_args_resolves_run_id` — `input.args:`
resolution, captured in step output even when CLI dispatch
is unavailable (the artifact-metadata use case).
- `test_switch_expression_matches_on_run_id` — switch
matches against the resolved value, proving the run id is a
first-class value in the expression engine, not just an
interpolation token.
- `test_workflow_without_context_reference_unchanged` —
locks the byte-equivalent default required by the issue.
### Docs
`workflows/README.md` gains a "Runtime Context" subsection
under "Expressions" documenting the new namespace and the
three canonical use patterns (telemetry, per-run scratch,
artifact metadata).
* test(workflows): drop inline double-quotes in run_id shell tests
`test_shell_run_resolves_run_id` and
`test_switch_expression_matches_on_run_id` used
`run: 'echo "RUN_ID={{ context.run_id }}"'` with inner double-quotes
around the echo argument. Bash/sh strips those quotes before invoking
echo, but cmd.exe (used on Windows when `shell=True`) treats them
as literal characters and emits `"RUN_ID=abc12345"` — failing the
exact-match assertion. Linux passed; all three Windows-latest matrix
entries failed with `assert '"RUN_ID=abc12345"' == 'RUN_ID=abc12345'`.
Resolve by dropping the inner double-quotes (the value has no spaces
or shell metacharacters) and wrapping the YAML scalar in plain
double-quotes the same way other shell-step tests in this file do
(e.g. `run: "echo b-saw-..."`). Behaviour-equivalent on POSIX,
portable to cmd.exe.
|
||
|
|
66884db85b |
fix: resolve __SPECKIT_COMMAND_*__ refs in preset skill rendering (#2717) (#2718)
* fix: resolve __SPECKIT_COMMAND_*__ refs in preset skill rendering (#2717)
The preset skill layer mirrors command templates into SKILL.md files but
only ran resolve_skill_placeholders(), leaving command cross-references as
raw __SPECKIT_COMMAND_<NAME>__ placeholders instead of rendering them as
/speckit-<cmd> the way CommandRegistrar.register_commands() does. As a
result, presets that override core commands under the agent skill layer
(e.g. Claude --ai-skills) leaked the raw tokens into SKILL.md.
Add a shared PresetManager._resolve_skill_command_refs() helper that maps
the agent's invoke separator to IntegrationBase.resolve_command_refs(), and
call it right after resolve_skill_placeholders() in every preset
skill-rendering path: _register_skills() (install), the _reconcile_skills()
override-restoration block, and both _unregister_skills() restore paths.
This mirrors register_commands() and addresses the path divergence flagged
in #1976.
Add regression tests covering the install and restore paths.
AI assistance: authored with Claude Code (Anthropic) — analysis, patch, and
tests. Verified via the existing pytest suite plus a manual CLI install and
remove cycle on a Claude --ai-skills project.
* test: cover reconcile-override and extension restore command-ref paths (#2718 review)
Copilot review flagged that the install and core-template restore paths
gained regression tests, but the reconcile project-override branch and the
extension-backed restore branch were uncovered. Add focused tests for both:
- test_reconcile_override_skill_resolves_command_refs: a project override
wins after preset removal; _reconcile_skills must render command refs.
- test_extension_restore_resolves_command_refs: a skill restored from an
extension command body must also render command refs.
Both fail on main and pass with the fix in
|
||
|
|
9af5411b4e |
Add Workflow Preset to community catalog (#2725)
* Add Workflow Preset to community catalog Add workflow-preset submitted by @bigsmartben to: - presets/catalog.community.json (alphabetical order) - docs/community/presets.md community presets table Closes #2618 * Fix Requires column: use — for no required extensions The Requires column lists required extensions, not the Spec Kit version. This preset has no extension dependencies. |