Integrate upstream github/spec-kit v1.0.6 - #124
Merged
Merged
Conversation
* chore: bump version to 1.0.4 * chore: begin 1.0.5.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Add Axi Extension to community catalog Add axi extension submitted by @d0whc3r to:\n- extensions/catalog.community.json (alphabetical order)\n- docs/community/extensions.md community extensions table\n\nCloses github#3948\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) * Add tools requirement to catalog community JSON Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Ken Schlobohm <keschlob@microsoft.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* allow hyphen in command ref token names the token pattern was matching only A-Z 0-9 and underscore so a command name like speckit.agent-context.update can not be written as a token and the token stays in the output as plain text now the character class allows a hyphen also in both places that resolve the token * document that a hyphen stays inside a segment the guide still said the token scheme does not carry hyphens, which is the opposite of what this branch does. added the real bundled command as the example since speckit.agent-context.update is the one that was unreachable before this. * replace the skills mode limitation with what skills mode actually does the callout said a command ref token reaches codex zcode and kimi verbatim. it does not. _resolve_command_ref_tokens inside _register_extension_skills resolves the same token shape against the active skill style. the callout was right that resolve_command_refs is never called there and wrong about what follows from it. * name the right invocation for each skills agent the callout said kimi renders the bare slash form. it does not, kimi is in SKILL_COLON_AGENTS only and falls through both branches to its own build_command_invocation which returns /skill:speckit-<name>.
…owerShell twins (github#4286) * fix(scripts): make bash branch-name sanitizing match the Python and PowerShell twins * fix(scripts): use ASCII acronym boundaries in the Python and PowerShell twins * test(scripts): cover the ASCII acronym boundary in the PowerShell twins The ASCII-lookaround fix in scripts/powershell/create-new-feature.ps1 and extensions/git/scripts/powershell/create-new-feature-branch.ps1 had no PowerShell regression coverage: the accented-acronym parity cases invoked only bash and Python, and the existing PowerShell acronym tests used ASCII-separated words, so a regression there would have passed CI. Adds a three-way bash/Python/pwsh assertion for 'Fix eDBe sync' in the core parity suite and a pwsh arm to the extension parity test, both asserting 001-fix-db-sync. * test(scripts): cover clean_branch_name independently of generate_branch_name
* feat(workflows): add plugin slots Assisted-by: GitHub Copilot (model: gpt-5.6-terra, autonomous) * fix(workflows): add runtime fan-out guard to PluginStep Mirror GateStep's inside_fan_out check so plugin slots inside fan-out templates fail at execution time, not only at static validation. This closes the gap when WorkflowEngine.execute() is called without prior validation. Assisted-by: opencode (model: qwen3.7-max, supervised) * refactor(workflows): rename plugin slots to workflow slots The feature reserves a no-op position replaced through a workflow overlay; it does not register or resolve plugins. Rename per maintainer feedback so 'plugin' stays available for a future genuine plugin mechanism and avoid confusion with Spec Kit extensions: - type: plugin -> type: slot - PluginStep -> SlotStep - 'plugin step/slot' -> 'workflow slot' in prose and error messages - steps/plugin/ -> steps/slot/ (git mv) - test_plugin_step.py -> test_slot_step.py (git mv) Assisted-by: opencode (model: glm-5.3-flash, autonomous) --------- Co-authored-by: Markus <markus@example.com>
Muse Code is Meta's terminal coding agent (binary: muse). It discovers project skills at .agents/skills/<skill-id>/SKILL.md and invokes them via the /speckit-<command> slash shortcut, so wire it up as a SkillsIntegration sharing the .agents/skills layout with Codex/Zed (multi_install_safe=False, same policy as docker-agent). Includes registry wiring, invocation-style mapping, init next-steps, discovery catalog, integrations doc, agent-context default (AGENTS.md), issue templates, and a dedicated test module.
…4401) * fix: drop unused scope input from the bundled speckit workflow The Full SDD Cycle workflow prompted for full / backend-only / frontend-only, but none of the steps read inputs.scope. Every command only received inputs.spec, so the three choices behaved the same. Remove the dead input from the shipped workflow and align the docs that showed `specify workflow run speckit ... -i scope=...`. Keep scope as an example in the generic input-typing docs for authors who do wire it up. * Bump bundled speckit workflow to 1.0.1 after dropping scope. Catalog installs only see the dead input removed when the published version advances past what they already have. --------- Co-authored-by: Gyanu <gyanum.ug20.cse@gmail.com>
Add evaluator extension submitted by @tbitcs to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#4414 Assisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ithub#4149) Both loop steps type-check `steps` ("must be a list") but never require it to be present, so an absent body silently becomes `[]`. `if` already requires `then`, and `fan-out` already requires both `items` and `step`. The mistype is unusually easy here because the fan-out step's own payload key is the singular `step:` while the loops use `steps:`. Writing `step:` on a `while` passed `specify workflow validate` with zero errors: A. while, body key typo'd as singular step: validate: [] execute : StepStatus.COMPLETED | next_steps = [] B. do-while, no steps at all: validate: [] execute : StepStatus.COMPLETED | next_steps = [] At run time the step reports COMPLETED while returning no `next_steps`, so the engine's `if result.next_steps:` block never fires and the loop the workflow is built around never runs even once. `DoWhileStep`'s own docstring promises "The first invocation always returns the nested steps for execution". Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…thub#4397) * fix(scripts): name setup-plan's feature directory key FEATURE_DIR setup-plan emitted a key called SPECS_DIR holding $FEATURE_DIR -- the per-feature subdirectory, not the specs root. The name is already taken elsewhere with the other meaning: create-new-feature.sh sets SPECS_DIR="$REPO_ROOT/specs" and derives FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME". setup-plan was also the only script in the suite using it. setup-tasks and both check-prerequisites payloads already emit FEATURE_DIR for exactly this value, so this brings setup-plan in line rather than inventing a convention. Renamed in all three ports so the payloads stay identical, and in templates/commands/plan.md, which is the only consumer -- it parses the key by name, so it has to move in the same commit. Verified the bash, PowerShell, and Python variants all emit ['BRANCH','FEATURE_DIR','FEATURE_SPEC','IMPL_PLAN']. Fixes github#4017 * test(scripts): pin setup-plan's FEATURE_DIR output contract Addresses review feedback. The existing setup-plan tests compare the ports against each other, so all three could regress to SPECS_DIR together and still pass. This asserts the contract absolutely, in JSON and text mode and across bash/Python/PowerShell: the key is FEATURE_DIR, it carries the feature directory rather than the specs root, and SPECS_DIR is absent. The value is matched by suffix rather than full path because the ports legitimately differ in path flavour -- under MSYS bash reports /tmp/... where the Python and PowerShell ports report C:\... . The suffix still separates specs/001-my-feature from a bare specs, which is the regression being guarded; verified it rejects both /tmp/proj/specs and C:\proj\specs.
…ithub#4424) docs/reference/workflows.md introduces its YAML block as the workflow that ships with Spec Kit, so a reader is entitled to treat it as the real definition. It had drifted on four points: version 1.0.0 -> 1.0.1 speckit_version >=0.7.2 -> >=0.8.5 integrations.any copilot, claude, gemini -> also alquimia, opencode integration default "copilot" -> default "auto" The last is the most user-visible: the guide stated the default integration was copilot, when it is auto, resolved from the project's initialized integration. Someone reading the guide to learn what they get by default was being told the wrong thing. Adds a guard so this cannot drift again. It compares parsed YAML rather than text, so the guide stays free to format lists however reads best and only the content has to agree. Verified it fails against the pre-sync copy, reporting all four differences, and passes after. Follow-up to github#4384 / github#4398, at the maintainer's suggestion.
…4320) * fix(presets): reject falsy non-mapping catalog config shapes `PresetCatalog._load_catalog_config` had two "shape check runs after an emptiness check" bugs, both masking a corrupted preset-catalogs.yml as an empty/no-op config instead of raising: - Top level: `yaml.safe_load(...) or {}` coerced a FALSY non-mapping document (`[]`, `false`, `0`, `''`) to `{}` before the `isinstance(data, dict)` guard ran, so it was silently treated as "no config" — while a TRUTHY non-mapping (a bare string) already raised "expected a mapping at root". - One level down: `catalogs_data = data.get("catalogs", [])` followed by `if not catalogs_data: return None` ran the emptiness check *before* the `isinstance(catalogs_data, list)` check, so a FALSY non-list `catalogs:` value (`{}`, `''`, `0`, `false`) was silently swallowed as "no catalogs" — while a TRUTHY non-list (`catalogs: "not-a-list"`) already raised "must be a list". `WorkflowCatalog._load_catalog_config` and `StepCatalog._load_catalog_config` (workflows/catalog.py) already guard against both cases correctly, with the same explanatory comments reused here. This preset sibling was missed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FW9fAYsCBCAgdKWovtSyqt * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix indentation in test for catalog config loading Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…te (github#4150) `ClineIntegration._inject_hook_command_note` guarded idempotency with a whole-document substring scan -- `if "replace dots" in content: return content` -- instead of the per-instruction check the shared `SkillsIntegration._inject_hook_command_note` helper uses (base.py:1637-1642), which compares only the line immediately above each match. Two consequences, both verified on main: A. unrelated prose -> note injected? False (base: True) B. two hook sections, one already noted -> notes: 1 (want 2) (a) Any command or extension markdown whose prose happens to contain the phrase "replace dots" loses the note entirely, so the generated workflow tells the agent to emit `/speckit.git.commit` -- a dotted command Cline never registers, since Cline installs `speckit-git-commit.md`. (b) A document with one already-noted hook section never gets a note on a second, un-noted one -- exactly what the base helper was changed to handle. Also aligns the capture group with the base helper: `^([ \t]*)` rather than `^(\s*)`. Because `\s` matches newlines the captured "indent" could swallow a preceding blank line, which was then re-emitted between the note and the instruction: '## Hooks\n\n <note>\n\n - For each executable hook, ...' The per-instruction check cannot line up until this is fixed. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…#4321) * fix(workflows): reject falsy non-mapping step.yml in step add `workflow_step_add` parses a fetched `step.yml` with `_yaml.safe_load(...) or {}`, which coerces a FALSY non-mapping top-level document (`[]`, `false`, `0`, `''`) to `{}` before the `isinstance(meta, dict)` shape check runs. The command then proceeds with `meta = {}`, derives `step_meta = {}` and `type_key = ""`, and reports the unrelated "step.yml missing 'step.type_key' field" instead of the real problem: "step.yml must be a YAML mapping". A TRUTHY non-mapping document (a bare string) already reported the correct error — this was an inconsistency. Same falsy-or-coerce shape as the catalog-config bugs fixed elsewhere in workflows/catalog.py, presets/__init__.py, and integrations (github#4187) this cycle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FW9fAYsCBCAgdKWovtSyqt * fix(workflows): distinguish explicit YAML null from empty step.yml An explicit null document (null/~/NULL) parses to the same None as a genuinely empty document, so it was silently coerced to {} and misreported as the unrelated "missing step.type_key" error instead of the mapping-shape error, per Copilot review on PR github#4321. Use yaml.compose to tell the two apart, matching the sibling loaders (yamlio.py, integrations/catalog.py, overlays/layer_sources.py). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147sii7uC56YzAu2Ep9qH94 * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… tasks (github#4430) * fix(tasks): require field constraints from data-model.md in generated tasks /speckit.tasks mapped data-model.md entities to user stories but never told the agent to carry field-level constraints (max length, nullable, enum values, validation rules) into the task text. Left to discretion, the implementing agent can silently invent its own value instead of the one recorded in data-model.md. Fixes github#4383 * test(tasks): tighten constraint regression assertion to match verbatim wording Addresses Copilot review feedback that the prior assertion only checked for the word 'constraint', so it would still pass if the rule's meaning were reversed (e.g. constraints permitted to be omitted).
Bumps the codeql-action group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.37.8 to 4.37.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@db488dd...cdf488f) Updates `github/codeql-action/analyze` from 4.37.8 to 4.37.9 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@db488dd...cdf488f) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-action - dependency-name: github/codeql-action/analyze dependency-version: 4.37.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-action ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Update brownkit extension submitted by @MaksimShevtsov: - extensions/catalog.community.json (version, download_url, metadata) - docs/community/extensions.md community extensions table Closes github#4435 Assisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update okf extension submitted by @alexcpn:\n- extensions/catalog.community.json (version, download_url, metadata, and tool requirements)\n- docs/community/extensions.md community extensions table\n\nCloses github#4439\n\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous)\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 1.0.5 * chore: begin 1.0.6.dev0 development --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…thub#4402) * docs: resolve assess clarifications by editing artifacts in place The assess README and decide command told people to re-run an earlier stage when decision.md said needs-clarification. That fights the sequential intake → research → define → shape → decide flow: each command normally runs once and leaves an editable Markdown file. Document direct edits and free-form agent updates as the default path, including refining decision.md itself, and keep stage re-runs as the exception for bad or discarded drafts. * Clarify needs-clarification as in-place artifact refinement. Route the assess diagram through a refine step back to decide, and list decision.md among the artifacts that path can update. * Narrow assess clarification docs to user-editable artifacts Keep speckit.assess.decide's existing output template. Clarifications are filled in the Markdown after the command finishes, not by changing what the command writes. --------- Co-authored-by: Gyanu <gyanum.ug20.cse@gmail.com>
Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ep refresh (github#4139) * fix(bundler): re-read the step registry when rolling back a failed refresh `_StepKindManager.refresh` documents that it keeps a backup and restores it "if the remove+reinstall path fails". The package half of that rollback works; the registry half was unreachable. `StepRegistry.__init__` snapshots the file once (`self.data = self._load()`) and `is_installed` consults only that snapshot. Measured: snapshot at construction: is_installed('my-step') = True after the entry is deleted on disk: same object = True <-- stale a fresh StepRegistry: = False By rollback time `self.remove()` has already deleted the entry from disk, but `self._registry`'s snapshot still contains it — so `not self._registry.is_installed(...)` was always False and the restore never ran, in exactly the failure case it was written for. The user was left with the step package back on disk but unregistered: `workflow step list` no longer shows it, the engine cannot resolve it, and a later `workflow step add <id>` refuses with "Step directory already exists". Read the registry fresh at rollback time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(bundler): restore the step registry entry verbatim on refresh rollback Addresses review feedback: the rollback used `StepRegistry.add()`, which does not restore the saved metadata verbatim. The rollback deliberately constructs a *fresh* `StepRegistry` after `self.remove()` has deleted the entry from disk (that re-read is this PR's actual fix). `add()` therefore finds no existing record: metadata_to_store["installed_at"] = existing.get( "installed_at", datetime.now(timezone.utc).isoformat() ) metadata_to_store["updated_at"] = datetime.now(timezone.utc).isoformat() `existing` is `{}`, so `installed_at` falls through to `now`, and `updated_at` is overwritten unconditionally. A failed refresh still mutated the installation metadata instead of rolling it back: seeded : installed_at 2020-01-01..., updated_at 2020-02-02... after add() : installed_at 2026-08-27..., updated_at 2026-08-27... verbatim via add() -> False verbatim via direct restore -> True Restore the entry directly and save, matching the existing rollback in `workflow_step_remove`, whose comment names this very hazard: "Restore the original registry entry verbatim (bypass add() which would overwrite timestamps)." The regression test now seeds distinctive past timestamps and asserts the restored entry equals the seeded one, rather than only asserting presence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ary, review-only material (github#4431) (github#4432) * fix(templates): stop /constitution from stacking Sync Impact Reports (github#4431) Step 4 of the constitution command only said to prepend the Sync Impact Report as an HTML comment, with no instruction to remove a previous one. Each run therefore added another report block on top of the last, growing the raw file (and the token cost of reading it) without bound. Now the step explicitly requires removing any existing report comment before adding the new one. Assisted-by: Claude (model: claude-sonnet-5, autonomous) * docs(templates): state the Sync Impact Report's temporary lifecycle Per review on github#4432/github#4431: the growth this fixed isn't a functional bug in the intended workflow — the report is scratch material for human review and is expected to be removed before the constitution file is committed. Step 4 now says so explicitly, alongside the existing replace-not-stack instruction for runs where a prior report was left in place. * docs(templates): drop the Sync Impact Report de-dupe instruction Per review, Step 4 should stay documentation-only: the report is temporary, review-only material removed before commit, so /constitution shouldn't auto-strip a pre-existing HTML comment (which could delete unrelated leading content). Retarget the regression test to pin the documented lifecycle instead of the removed de-dupe behavior.
* Add concise code review skill Add repository-specific guidance for positive and negative tests, regression evidence, and terminology alignment while exempting community-authored catalog content. Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 60c0e36f-daf2-4c62-87ee-4da8fb1eaf12 * Clarify community content exception Exclude community-authored catalog content and generated documentation from terminology alignment reviews. Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 60c0e36f-daf2-4c62-87ee-4da8fb1eaf12 * Clarify test coverage principle Require test cases to verify intended behavior and what each code change should prevent. Assisted-by: GitHub Copilot (model: gpt-5.6-luna, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 60c0e36f-daf2-4c62-87ee-4da8fb1eaf12 --------- Copilot-Session: 60c0e36f-daf2-4c62-87ee-4da8fb1eaf12
…ng hooks silently (github#4456) * fix(templates): report an unreadable extensions.yml instead of skipping hooks silently All ten core command templates told the agent: "If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally". A `.specify/extensions.yml` with one malformed line was therefore treated exactly like a manifest with no hooks, and a mandatory hook (`optional: false`, as registered by the bundled git extension) was disabled without anything saying so. The agent now tells the user that the manifest could not be read (with the parser error) and that no hooks were checked, then continues as before. Control flow is unchanged; only the silence is removed. Adds tests/test_command_template_hooks.py, which pins the wording at both hook sites of every template that reads extensions.yml. * test(templates): pin every clause of the parse-failure instruction The regression test only pinned two of the five clauses the new instruction carries. Dropping the parser error, the mandatory-hook warning or the continuation clause from every template would have left it green. It now pins all five: the manifest could not be read, the parser error is included, no hooks were checked, mandatory (optional: false) hooks are named, and the command then continues. Checked by mutation: removing any one clause from all ten templates fails the ten parametrized cases.
…tension changes (github#4395) * ci(extensions): guard bundled extension changes behind a version bump Bundled extensions only reach existing installs through a version bump: `specify extension update` compares the semver in extensions/catalog.json against the installed copy and reports "Up to date" whenever they match. Content changes shipped without a bump go silently stale on every project that already installed the extension (github#4345). This guard turns "please remember to bump" into a merge requirement. - `.github/scripts/check_extension_version_bump.py` fails a PR that changes files under `extensions/<id>/` for a catalog-listed extension without increasing that extension's `extension.yml` version (PEP 440 comparison, the same semantics `extension update` uses), and requires `extensions/catalog.json` to stay in sync with each manifest. Non-catalog extensions (the `selftest` fixture and the `template` scaffold) are exempt. - `extension-version-guard.yml` runs the check on pull requests touching `extensions/**`, diffing the PR base against HEAD. - Contract tests pin the working-tree half of the invariant (catalog/manifest version sync, bundled entries shipping a directory); guard-script tests pin the failure behavior against real throwaway git repositories so a parsing change cannot silently disable the guard while CI stays green. Split out of github#4351 (part 3 of the series requested in review); refs github#4345. Assisted-by: Claude Code (model: claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): make the extension version guard usable as a required check and quoting-proof Address Copilot review round 1 on github#4395. Workflow: drop the `paths: extensions/**` filter. A required status check that is skipped by path filtering stays in "Expected" state and blocks every PR that does not touch extensions/**, which defeats the point of making the guard a merge requirement. The job now runs on every pull request; the script already reports success when nothing under extensions/ changed, so unrelated PRs pass in one short job. Script: read the changed-path list with `git diff --name-only -z`. With git's default core.quotePath, a path containing non-ASCII or control characters is C-quoted with the quotes included (`"extensions/demo/caf\303\251.txt"`), so its first component was no longer `extensions` and an unbumped change to such a file escaped the guard. NUL-delimited output is emitted verbatim; paths are decoded with surrogateescape so an undecodable byte cannot crash the check, and only the ASCII `extensions/<id>/` prefix is ever interpreted. Tests: pin both behaviors. The non-ASCII case fails against the previous script and passes now; the no-extension-changes case backs the workflow change. core.quotePath is pinned to true in the fixture repo so the regression exercises the quoting path even where a developer's global config disables it. Refs github#4345 Assisted-by: Claude Code (model: claude-fable-5-1) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ci): validate a new extension's version even without a base to compare Address @mnriem's review on github#4395. For a brand-new extension the guard returned early at "no base manifest" before ever parsing the head version, and the catalog check only tests string equality. A new extension shipped with e.g. `not-a-version` in both extension.yml and catalog.json therefore passed the guard, although ExtensionManifest rejects a version packaging cannot parse (ValidationError "Invalid version") and `extension update` skips catalog entries whose version is invalid - the extension would be uninstallable and never updatable. Parse the head version before the new-extension early return and fail closed with a dedicated message when it is not PEP 440. The base version is now parsed separately, so an unparseable base still fails with the "could not compare versions" message. Tests: add test_new_extension_with_unparseable_version_fails (fails against the previous script, passes now) and point the existing unparseable-version test at the new head-version message, which fires first for that scenario. Refs github#4345 Assisted-by: Claude Code (model: claude-fable-5-1) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ci): diff the extension version guard against the merge commit's first parent The guard diffed github.event.pull_request.base.sha against the checked-out pull-request merge commit. Those two are not built from the same base: the payload SHA is the base-branch tip from when the PR was opened and is not refreshed when the base moves, while refs/pull/N/merge is rebuilt against the current tip. Any base-branch drift between the two is then attributed to the PR. Observed on github#4395 itself: base.sha was main as of Sep 1 (3eec154), the merge commit was built on main as of Sep 3 (db64869), and two unbumped bundled-extension changes merged in between (github#4413 touching agent-context, github#4286 touching git) failed the guard on a PR that never touched either. Fetch the merge commit with depth 2 so both parents are available and diff against HEAD^1, which is by construction the base the merge was built on. Fail loudly if HEAD is not a merge commit. The script's CLI is unchanged; a regression test reproduces the trap in a throwaway repo (the stale base blames the drift on the PR, HEAD^1 does not). Refs github#4345 Assisted-by: Claude Code (model: claude-fable-5-1) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(ci): validate every catalog version, not only those of changed extensions Address the "needs a closer look" items from the Copilot reviews on github#4395. Invariant 2 skipped catalog entries without an in-repo directory before parsing their version, and never parsed the in-repo manifest version at all - PEP 440 validation lived only in Invariant 1, which sees just the IDs with changed files under extensions/<id>/. Two gaps followed: - a hosted (catalog-only) entry could carry `"version": "not-a-version"` and pass, although `extension update` skips exactly such entries when packaging.Version fails; - promoting an existing uncataloged directory by adding only its catalog entry never entered Invariant 1, so a matching invalid string in manifest and catalog passed the plain equality check. Invariant 2 now runs over every catalog entry: the catalog version must be a non-empty string that parses as PEP 440, and for entries with an in-repo directory the manifest version must parse as well before the equality check. The catalog-side parse alone closes the promotion gap (a valid catalog string plus equality forces a valid manifest string); the manifest parse makes the failure name the manifest precisely. Tests: `_run_guard` now omits the head argument by default, matching the workflow's one-argument invocation, with a separate test for the optional HEAD_REF; new cases cover the hosted-entry and both promotion shapes (the two invalid ones fail against the previous script) plus a valid promotion that must keep passing. The working-tree contract test gains the matching every-catalog-version-parses check. Refs github#4345 Assisted-by: Claude Code (model: claude-fable-5-1) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Jakub Baranowski <cr4zybaran@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Update figma-starter extension submitted by @vibhus:\n- extensions/catalog.community.json (version, download_url, metadata)\n- docs/community/extensions.md community extensions table\n\nCloses github#4463\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Update pay-x402 extension submitted by @shawn93676: - extensions/catalog.community.json (version, download_url, description, commands, tags) - docs/community/extensions.md community extensions table Closes github#4446 Assisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thub#4485) Add pdac-product extension submitted by @juangcarmona to: - extensions/catalog.community.json (alphabetical order) - docs/community/extensions.md community extensions table Closes github#4453 Assisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: preserve extension authors in generated skills Carry extension author metadata through skill registration, aliases, and restoration after preset removal. Keep the existing default for core skills and extensions without an author. Assisted-by: OpenAI Codex (model: GPT-6, autonomous) * test: accept author in extension registrar stub Assisted-by: OpenAI Codex (model: GPT-6, autonomous) * docs: place author parameter description on registrar Assisted-by: OpenAI Codex (model: GPT-6, autonomous) * fix: normalize extension skill authors Assisted-by: OpenAI Codex (model: GPT-5, autonomous) --------- Co-authored-by: 胥寅 <xuuyin@dingtalk.com>
* feat(workflows): add per-step integration configuration Assisted-by: OpenAI Codex (model: GPT-5, autonomous) * fix(workflows): address per-step integration review feedback Assisted-by: OpenAI Codex (model: GPT-5, autonomous) * fix(workflows): let per-step Docker Agent args override legacy config Assisted-by: OpenAI Codex (model: GPT-6, autonomous) --------- Co-authored-by: 胥寅 <xuuyin@dingtalk.com>
…4478) * docs: document contribution evidence gate and label taxonomy Add an "Evidence gate" subsection, a high-level "Review rubric" (D1-D7, scored 0-2 each), and a "Triage and author labels" reference to CONTRIBUTING.md so the acceptance bar is discoverable before contributing. Cross-links the existing AI-contributions section and three-open-PR cap rather than restating them. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: expand author labels, catalog channel, and close expectations Add author-needs-proof and author-needs-rebase to the author-actions table, a "Community catalog submissions" subsection pointing catalog changes at the submission-issue flow, and a note on when items are closed as triage-out-of-scope. Fix the author-awaiting phrasing ("handed off to"). author-needs-rescope is intentionally omitted: it does not yet exist on github/spec-kit, so it is flagged for the maintainer rather than documented as a live label. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: add author-needs-rescope to author labels table Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix typo in CONTRIBUTING.md regarding AI-sourced contributions Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Update speckit-inventory extension submitted by @Yash-Chindam:\n- extensions/catalog.community.json (version, download_url, sha256, etc.)\n- docs/community/extensions.md community extensions table\n\nCloses github#4486\n\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous)\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Update Inventory Alignment preset to v0.1.1 Update inventory-alignment preset submitted by @Yash-Chindam:\n- presets/catalog.community.json (version, download_url, sha256, requires, timestamps)\n- docs/community/presets.md community presets table (no row changes required)\n\nCloses github#4487\n\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous)\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Inventory Alignment catalog version Assisted-by: GitHub Copilot (model: GPT-5.4, autonomous) Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: revert unrelated canon-core preset version bump Keep canon-core at 0.1.0 so its version matches its release asset. Limit the preset update to inventory-alignment. Assisted-by: GitHub Copilot (model: gpt-6-astra, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
* Update Spec Kit Schedule extension to v0.7.4 Update schedule extension submitted by @jfranc38:\n- extensions/catalog.community.json (version, download_url, metadata)\n- docs/community/extensions.md community extensions table\n\nCloses github#4457\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\nAssisted-by: GitHub Copilot (model: gpt-5.2-codex, autonomous) * Apply suggestion from @KSchlobohm --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Ken Schlobohm <keschlob@microsoft.com>
…hub#4503) Update the stale workflow so issues and PRs are marked stale after 60 days of inactivity and closed 30 days later (90 days total), down from 150/30. Messages updated to match. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…irectory (github#3786) * docs(core): SPECIFY_FEATURE sets the feature label, not the feature directory docs/reference/core.md told users to set SPECIFY_FEATURE "to the feature directory name ... to work on a specific feature when not using Git branches". That does not work: SPECIFY_FEATURE only feeds get_current_branch / Get-CurrentBranch (the feature *label*). The directory comes from SPECIFY_FEATURE_DIRECTORY or .specify/feature.json. Verified on main with the real helper -- with ONLY SPECIFY_FEATURE set: $ SPECIFY_FEATURE=001-photo-albums ... get_feature_paths ERROR: Feature directory not found. Set SPECIFY_FEATURE_DIRECTORY or run the specify command to create .specify/feature.json. exit=1 $ SPECIFY_FEATURE_DIRECTORY=specs/001-photo-albums ... get_feature_paths FEATURE_DIR -> <resolved> CURRENT_BRANCH -> 001-photo-albums The code's own error message points at the other variable, and the doc's own "Two resolution axes" note directly below already says the feature is selected by SPECIFY_FEATURE_DIRECTORY / .specify/feature.json -- so the table row contradicted both the code and the paragraph under it. Describe what the variable actually does, note that /speckit.specify and the Git extension normally set it, and point at the directory axis. Docs only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(core): describe SPECIFY_FEATURE as an explicit label override The row still misstated when and how the label is applied: * "when there is no Git branch context" — get_current_branch and Get-CurrentBranch never inspect Git at all. They return the variable verbatim when set, and otherwise fall back to the basename of the resolved feature directory. * "Normally set for you by /speckit.specify" — specify.md persists feature_directory to .specify/feature.json and never sets this variable. * The Bash and Python feature scripts can only *print* a commented export hint, because a child process cannot change its parent's environment. The PowerShell scripts do assign $env:SPECIFY_FEATURE, but only reach the caller when run inside the current session. Rewrite it as an explicit user-set label override, and distinguish the printed persistence hint from actually setting the caller's environment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(core): attribute the label fallback to get_feature_paths, not get_current_branch The row said the label "falls back to the basename of the resolved feature directory" when SPECIFY_FEATURE is unset, and attributed that to get_current_branch / Get-CurrentBranch. Those helpers return an EMPTY string when the variable is unset — scripts/bash/common.sh:87 says so outright ("Return empty to signal 'unknown'") and scripts/python/common.py is `return os.environ.get("SPECIFY_FEATURE", "")`. The basename substitution happens later, in get_feature_paths / Get-FeaturePaths, after the feature directory has been resolved (scripts/python/common.py:168-169). Measured: get_current_branch (unset) -> [] get_current_branch (set) -> [my-label] get_feature_paths CURRENT_BRANCH -> [001-photo-albums] So a caller invoking the named helpers directly does not get the fallback. Distinguish the two behaviours. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(core): name the PowerShell helper Get-FeaturePathsEnv The doc cited `Get-FeaturePaths`, which does not exist. The PowerShell twin of `get_feature_paths` is `Get-FeaturePathsEnv` (scripts/powershell/common.ps1:152); there is no bare `Get-FeaturePaths` anywhere in the tree. Verified every function name the entry cites now resolves against the scripts: get_current_branch, Get-CurrentBranch, get_feature_paths, Get-FeaturePathsEnv. The quoted resolution error is verbatim from scripts/bash/common.sh:206. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…command (github#4337) * fix(events): cap stdin in the generated dispatcher, not just the CLI command The github#3857 fix capped stdin at 1 MiB in `specify event run` (src/specify_cli/commands/event.py), but that command is not the code path native hooks actually invoke. Every installed integration writes a self-contained `.specify/events.py` dispatcher (the `_EVENTS_DISPATCHER_TEMPLATE` string in src/specify_cli/events.py) that native hook configs call directly, and its `main()` did: payload = sys.stdin.read() if not sys.stdin.isatty() else "{}" with no size cap at all — the exact DoS github#3857 was meant to close, wide open on the primary invocation path. `specify event run` is a secondary/manual entry point; the generated dispatcher is what actually runs on every session_start/pre_tool_use/etc. hook fire in real usage. Fix: apply the same byte-capped read (from the binary buffer, so the cap counts encoded bytes rather than decoded characters — matching the just-merged fix for the CLI command) inside the dispatcher template, so every newly-installed or refreshed dispatcher enforces the limit. ## Test plan - Added 3 tests in tests/integrations/test_events.py::TestCommandRunner: an oversized payload exits 1 with the limit message instead of running unbounded, a multibyte payload (~300k emoji, ~1.14 MiB UTF-8 but only 300k characters) is still rejected by the byte-based cap, and a normal under-the-cap payload still reaches the handler script unchanged. - Verified both new failing-without-fix tests via test-the-test (stashed the src fix): the oversized-payload test failed because the dispatcher silently accepted the full payload and returned "not found" instead of exiting 1 with the limit message — reproducing the exact bug. - Ran the full tests/integrations/test_events.py suite (124/128 pass; the remaining 4 are the pre-existing Windows symlink-elevation failures unrelated to this change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJHJ2dHP2RVCNncHqN8Qm9 * fix(events): pin utf-8 encoding on the handler subprocess in both dispatch paths Addresses Copilot review feedback on PR github#4337: Both `_run_inline` (the generated dispatcher's stdlib fallback) and `resolve_and_run_event_command` (the delegated/CLI-native path) decode stdin explicitly as utf-8, then pass that string to the handler via `subprocess.run(..., text=True)` with no explicit `encoding=`. Without one, `text=True` re-encodes the payload for the child's stdin using `locale.getpreferredencoding()` — on Windows that's commonly the ANSI codepage, not UTF-8 — so a non-ASCII payload byte (e.g. "é") reaches the handler as the wrong byte, corrupting JSON for handlers that expect UTF-8. Pin `encoding="utf-8"` on both subprocess.run calls so the decode and re-encode agree. Also rewrote `test_dispatcher_underlimit_stdin_still_runs` (previously skipped entirely on Windows via a POSIX-only `sh` handler) to use a cross-platform Python handler and assert byte-for-byte fidelity of a non-ASCII payload, and added test_dispatcher_inline_fallback_preserves_non_ascii_payload, which forces the `_run_inline` fallback (never reached in a dev environment where specify_cli is importable, since the dispatcher always delegates first) so that path's fix is independently verified too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NhR6g8xT8at5pPMhkrC3e2 --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Release v1.0.6 # Conflicts: # CHANGELOG.md # pyproject.toml
Aligns version, fork_version, and project_version fields with upstream release v1.0.6 following the sync/upstream-v1.0.4 pattern (#116). Assisted-by: Jane Alesi (GLM, autonomous)
…tream content changes Upstream v1.0.5/v1.0.6 changed files under these bundled extensions without version bumps (the CI guard landed mid-cycle in v1.0.6, after those changes). The fork sync PR trips the new check_extension_version_bump guard; bumping satisfies it and - per the guard's own rationale - makes the changed content reach installed copies via 'specify extension update'. Assisted-by: Jane Alesi (GLM, autonomous)
- git extension manifest test asserted the literal 1.0.0 - bundler init/local-install fixtures pinned agent-context at 1.0.0; the install preflight compares bundle pins against catalog.json and now rejects the stale pin Assisted-by: Jane Alesi (GLM, autonomous)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Integrates upstream
github/spec-kitreleasesv1.0.5+v1.0.6intomain-speck, following the established sync pattern (#116 /sync/upstream-v1.0.4).098ac5d2(Merge tag 'v1.0.6' into sync/upstream-v1.0.6)specs/metadata.json->version: 1.0.6,fork_version: satware-v1.0.6,project_version: 1.0.6(af6abbbf)Conflict resolutions
pyproject.toml1.0.6)CHANGELOG.mdAuto-merged cleanly:
tests/integrations/test_integration_cline.py,tests/test_extension_skills.py.Notable upstream changes absorbed
feat(integrations): add Muse Code (muse) agent integration (feat(integrations): add Muse Code (muse) agent integration github/spec-kit#4413)feat(workflows): add workflow slots (feat(workflows): add workflow slots github/spec-kit#4352) and per-step integration configuration (feat(workflows): add per-step integration configuration github/spec-kit#4425)fix(cline): stop unrelated prose from suppressing the hook command note (fix(cline): stop unrelated prose from suppressing the hook command note github/spec-kit#4150) - relevant to our Cline integrationfix(tasks): require field constraints from data-model.md in generated tasks (fix(tasks): require field constraints from data-model.md in generated tasks github/spec-kit#4430)fix(bundler): re-read the step registry when rolling back a failed step refresh (fix(bundler): re-read the step registry when rolling back a failed step refresh github/spec-kit#4139)Verification
pytest tests/(own venv)scripts/daily-routine.sh pre-prOK: Up to date with upstream v1.0.6Closes #122, closes #123 (rolling upstream-sync issues).
Filed by @satwareAG-ironMike (Michael Wegener); authored by Jane Alesi (GLM via satware AI harness, autonomous session).