mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-09-14 20:16:53 +08:00
@fission-ai/openspec@1.13.0
843 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9d4e5974e5 |
Version Packages (#1822)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>@fission-ai/openspec@1.13.0 v1.13.0 |
||
|
|
e4e112d94f |
chore(deps): declare pnpm overrides only in pnpm-workspace.yaml (#1816)
The security overrides were declared twice: in pnpm-workspace.yaml, with the advisory comments explaining each pin, and again under package.json's pnpm.overrides. The copies are not additive — pnpm 10 uses package.json's block instead of the workspace list when both are present — and Dependabot rewrites plain-name entries in package.json whenever it bumps the same package. So a routine bump silently displaces the pins that patch advisories, and fails the equality test that guards them (#1812). Keeps one declaration, in the file that carries the reasoning, and asserts the mirror stays gone. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
aedf4d0c64 |
fix(archive): preserve blank lines inside code fences (#1798)
* fix(archive): preserve blank lines inside code fences * docs(test): document fence-preservation test helpers * chore(changeset): track the fenced blank-line fix The fix changes archive output for any spec documenting a fenced sample with consecutive blank lines, so it belongs in the changelog. Release tracking only validates changesets that exist; it never requires one, which is why CI stayed green without it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d9e1a28c38 |
fix(update): refresh generated files that drifted (#1808)
* fix(update): refresh generated files that drifted * test(update): isolate command drift from missing files and host config * chore(changeset): track the command-drift fix `openspec update` now reports and repairs tools it previously called up to date, so users will see a behavior change. That belongs in the changelog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8251763ecd |
fix(parser): apply every delta section header (#1802)
* fix(parser): apply every delta section header * test(parser): assert section presence for a header after another section * chore(changeset): track the repeated-section fix Deltas that previously applied only part of what was authored now apply all of it, which changes archive output for affected changes. That belongs in the changelog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fadac3e1c9 |
fix(parser): accept all CommonMark list markers in deltas (#1800)
* fix(parser): accept all CommonMark list markers in deltas * docs(parser): document the REMOVED and RENAMED readers * chore(changeset): track the list-marker fix A removal or rename written with `*` or `+` now takes effect where it previously did nothing, so existing specs can change on the next archive. That is a user-visible behavior change and belongs in the changelog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3915db763a |
fix(guidance): teach the spec-inventory verb to generated guidance (#1700)
* fix(guidance): teach the spec-inventory verb to generated guidance `openspec list --specs` appeared in no generated skill, command, or artifact instruction, while `openspec list --json` — the in-flight CHANGE list — appeared throughout. An agent asked to read the existing specs first reached for the one enumeration verb it had been taught, got the change list, found it plausible, and reported the step complete against the wrong object. Explore now lists the spec inventory alongside the change list and says which is which. The spec-driven `proposal` and `specs` instructions name the command at the two points that need it: researching existing capabilities before filling in the Capabilities section, and confirming a delta's path matches an existing capability. Guidance text only — no CLI, parser, or archive behavior changes. Closes #1689 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(guidance): carry the store qualifier wherever the command is named A bare `openspec list --specs` reads the local inventory, so under a selected store it confirms a capability path against the wrong root. The proposal instruction carried the qualifier; the modified-capability instruction did not. All four sites now use the same wording, and the guard is scoped to the passage that names the command — every explore body already carries the qualifier in its unrelated capture steps, so a whole-body assertion would pass with it dropped here. Addresses CodeRabbit review on #1700. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(guidance): read a listed capability with the store-aware command The read step I added defeated the fix under a store. It told the agent to list the inventory with `--store "<id>"`, then read the result back from `openspec/specs/<capability-path>/spec.md` — a local path. Verified against a registered store: `list --specs --store mystore` returns `store-only-capability`, and the corresponding local read fails outright (or, when a local capability happens to share the name, silently returns a different one). That is the same wrong-object failure #1689 is about, reintroduced one line later. Capabilities are now read with `openspec show "<spec-id>" --type spec --json --no-scenarios`, which resolves against the same root the listing came from and returns purpose plus requirement texts without pulling whole spec files into context. `--type spec` is load-bearing: a change and a spec sharing a name is an ambiguous_item error, and change names routinely mirror capability names. Also documents `--store` on `list` and `show` in docs/cli.md. Both already accepted the flag — the prose at line 228 says so — but neither options table listed it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: Use ASCII arrows instead of unicode This fixes the issue of ambiguous unicode character width when visualizing on terminals * fix: Update remaining docs within explore to use ASCII * fix(explore): finish the ASCII conversion and guard it Rebase onto main and close the gaps in the original fix: - Regenerate skills/openspec-explore/SKILL.md. The static skills/ mirror landed after this branch was cut, so the parity test would have failed with the template and the mirror out of sync. - Regenerate the three parity hashes through scripts/regen-parity-hashes.mjs. - Convert the ambiguous-width glyphs the first pass missed: the bullets in the CLI-storage example, and the check/cross marks in its comparison table, which sat in the column-aligned block the bug is about. - Tighten the ASCII guidance to two lines. It ships into every user project on both delivery surfaces, so the paragraph was pure overhead. - Add regression tests (#983): every fenced example in both the skill and the command body must be free of box-drawing, arrow, bullet, and check/cross glyphs, and the guidance must state the rule and the reason. - Add a patch changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(explore): cover every check/cross dingbat in the ASCII guard The matcher listed U+2713 and U+2717 only, so a fenced example could use ✕ (U+2715) or ✘ (U+2718) — same ambiguous width, same misalignment — and still pass. Widen to the U+2713-U+2718 run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(explore): require explicit confirmation before writing files * test(explore): harden write confirmation guardrail * fix(explore): scope write confirmation precisely * test(guidance): pin store-aware spec reads * test(templates): regenerate explore parity hashes The explore template now carries three independent guidance edits: the spec-inventory verb, the ASCII diagram conversion, and the write confirmation contract. Each pinned its own hash constants, so the pinned values no longer describe the combined template. Regenerate them from the merged source with `regen:parity-hashes` rather than hand-editing, and confirm the committed skills mirror still matches byte-for-byte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(guidance): read complete specs before coverage decisions * docs: drop the redundant legacy docs/cli.md edit docs-lab/README.md makes docs-lab/ canonical and the old docs/ tree legacy. docs-lab/reference/cli.md already documents `--store <id>` for both `openspec list` and `openspec show`, so this branch's docs/cli.md rows added a third copy in the stale tree and nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(changeset): drop the docs claim this PR no longer makes alfred-openspec on #1700: the release note still said docs/cli.md now documents --store on list and show, but that legacy-tree edit was removed from this head and the diff does not touch docs/cli.md. The canonical docs-lab/reference/cli.md already documented the flag on both commands, which is why the edit went. Removing the sentence rather than repointing it at docs-lab: nothing in docs-lab changed either, so there is no documentation change to announce. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Shooks <justanormalme@gmail.com> Co-authored-by: Ayman D. <ayman.bacc@gmail.com> |
||
|
|
c170dc77ad |
fix(archive): read a wrapped scenario bullet as one bullet (#1782)
* fix(archive): read a wrapped scenario bullet as one bullet A repository that wraps its prose at a column limit writes most scenario bullets over two lines. The retirement guard read the continuation line as content the merge could not account for, so `retire_capabilities` refused every such spec - and because the hint that names the marker is gated on that same count, an unmarked author got the bare "must have at least one requirement" abort and never learned the retirement path exists. A line indented to the content column of the item above it, with no blank line between, is part of that item. It is accounted for when the item was and already reported when it was not, so nothing is deleted unmentioned either way. A blank line still ends the item, so a note written below the scenarios is still the author's own however it is indented. Closes #1780 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): keep an indented heading out of a bullet's continuation Continuation is for wrapped prose. A raw HTML heading indented under a scenario bullet was absorbed by it, so indenting a section one level would have smuggled it past the audit and deleted it with the file. ATX headings were already excluded; HTML ones now are too, matching how the pass above the requirements section reads them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): flag a setext heading indented under a bullet A setext underline turns the line above it into a heading, so indenting the pair one level under a scenario bullet let a whole section be absorbed as continuation and deleted with the file. Checked ahead of the continuation branch now, the same way the ATX and raw HTML forms already are. Found by CodeRabbit on this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): read an unindented wrapped bullet as one bullet too Not every wrap indents its continuation, and the indent-only rule left the reported bug fixed for one spelling and live for the other: a hand-wrapped scenario bullet still refused the retirement. Inside a scenario's unbroken bullet run a lazy continuation is now read as part of the bullet above it. This widens nothing - a sibling bullet written in that same position is already read as the scenario's own, and a lazy line is part of the bullet where a sibling is merely next to it. Past the blank line that ends the run the indent is still required, so a note bulleted below the scenarios and the line that wraps it stay the author's. Also covers CRLF specs, and asserts the refusal report names only the real leftover in a wrapped multi-requirement spec rather than burying it under continuations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): stop a lazy continuation at anything that opens a block CommonMark lets a blockquote, thematic break, table, list item or raw HTML interrupt a paragraph, so one written flush against a scenario bullet starts something new rather than continuing it. The lazy allowance absorbed all of them, which would have deleted an author's note with the file and named nothing. The bullet's paragraph is now tracked as its own state: opened by a bullet, closed by a blank line, a fence, a heading, or a line that opens a block - including one indented inside the item, whose own paragraph ends the bullet's. Lazy continuation applies only while it is open. Indented continuation is unaffected: a nested list or quote sitting inside the item is still the item's own content. Each of the six holes is pinned by a test proven to fail with the narrower rule removed. Found by CodeRabbit on this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): classify a line as the list item sees it A marker as wide as `100. ` puts the item's content past the three columns a Markdown construct is allowed at the file's left margin, so `## Retention` written inside such an item read as five spaces of nothing and was absorbed as continuation - a regression against the behavior before continuation existed, which named it. Every syntax test in the audit now reads the line with the item's indent removed, so a heading, a setext underline or a block start is recognized wherever the item sits. Found by CodeRabbit on this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: drop test scratch directory committed by mistake `test-spec-command-tmp/` is a fixture a test run leaves behind, swept up by `git add -A` in the previous commit. It is not part of the change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(archive): share one list-marker definition with the paragraph rule Folds in the marker coverage from the duplicate PR #1789, which fixes the same issue (#1780) with a shallower model. The audit named `-`, `*` and ordered items as list markers, while INTERRUPTS_PARAGRAPH, added in this same PR, already named `+` and capped an ordered marker at CommonMark's nine digits. The two disagreed, so a line one called a bullet and the other did not was read as both at once. Both now use one LIST_ITEM constant: - `+` is the behavior fix. A spec bulleted with `+` validates like any other, and every one of its scenario bullets was reported as unaccounted content, so that capability could not be retired at all. Regression added, verified to fail against the old marker set. - The nine-digit cap changes no verdict in this design, since a line the pattern rejects is weighed by the same rules either way. It is here for the consistency, and the comment says so rather than claiming a fix. The case is pinned so a later change cannot start deleting such a note. LIST_ITEM also no longer requires content after the marker, so an empty `- ` reads as the bullet it is instead of falling through to the leftovers, which is what the surrounding indent tracking already assumed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8ba4ac1b16 |
fix(apply): warn when a change is ready to implement with no specs (#1783)
* fix(apply): warn when a change is ready to implement with no specs Apply gates on the schema's `apply.requires` (tasks) alone, so a change whose tasks file was written ahead of its specs read as ready even though it had no delta specs at all — the state `openspec validate` rejects. Apply was the one surface that green-lit a change every other surface flags, which is how agents end up implementing before the specs exist. Report it as a warning, in the text output and in `--json`, naming both ways out: write the specs, or declare `skip_specs: true`. Blocking would be a policy change; naming the gap is not. Changes that have specs, declare `skip_specs`, or are still blocked on their own required artifacts are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(apply): name the metadata file from its shared constant Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(apply): cover custom schemas in the no-specs warning A schema with no spec-producing artifact must stay quiet, and one whose spec artifact is not called `specs` must still warn - the rule keys off the output path, not the artifact id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(apply): stop asserting an absolute temp path on Windows os.tmpdir() hands back the short form (C:\Users\RUNNER~1) while the CLI resolves the long one, so the assertion pinned a path that never matched on windows-pwsh. Assert the change-relative tail instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(apply): name the whole chain a blocked change still needs Apply blocks on the schema's `apply.requires` alone, so its message stopped at the first hop: a change holding only a proposal was told "Missing artifacts: tasks" while the specs `tasks` depends on were missing too. Taken literally that is an instruction to write the tracking file straight from the proposal and skip everything between — the failure reported in #834 and #869. Walk `requires` and report the whole set, in build order, as `missingPrerequisites` (text and `--json`). What apply blocks on is unchanged, and the wording leaves conditional artifacts to the schema rather than demanding them. The remedies these messages give are now CLI commands rather than the `openspec-continue-change` skill: `continue` is not in CORE_WORKFLOWS, so on the default profile the old advice named a skill that is never installed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(apply): name the schema's own spec artifact in the warning alfred-openspec on #1783: collectApplyWarnings() discovers spec-producing artifacts by output path, so it correctly fires for a schema whose artifact id is `contracts`, but the remediation text then hardcoded `openspec instructions specs`. That names an artifact such a schema does not declare, so the advertised custom-schema support dead-ended at the exact step meant to resolve the warning. The command now derives its target from specArtifacts: the artifact's own id when the schema declares one spec-producing artifact, and `<artifact-id>` as a placeholder when it declares several, since there is no single right answer there and a guess would read as an instruction. The renamed-artifact test now asserts the command names `contracts` and rejects the hardcoded `specs` spelling, and a new test pins the two-artifact placeholder. Verified both fail against the hardcoded string. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(changeset): bump apply warnings to minor This adds `missingPrerequisites` and `warnings` to the documented `instructions apply --json` contract in docs/agent-contract.md. New fields are backward compatible, but they are new capability an agent can consume, which is a minor under semver rather than a patch. Taking the conservative direction deliberately: shipping new API surface as a patch is the violation, since a consumer pinned to a patch range would receive it without opting in. A minor costs nothing if the fields turn out to be uninteresting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3c6d318b83 |
fix(init): name the workflows the profile left out (#1779)
* fix(init): name the workflows the profile left out Setup output listed the workflows it installed but never mentioned the ones it did not, so a user on the default core profile who typed /opsx:ff saw nothing and read it as a broken install. The docs explain profiles; nobody reads them before typing a command that should be there. init now closes with the missing workflows by name and the two commands that add them. The note is skipped when nothing was generated at all, where the existing delivery correction is the whole story, and when the profile already installs everything. Also adds a troubleshooting entry for the "only some /opsx: commands show up" symptom, which the existing list did not cover. Relates to #1076 (the optional-workflow discoverability half; the Windows command-discovery repro in that thread is not addressed here) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore: drop a test scratch directory committed by mistake test-show-command-tmp/ is created by a test run and does not exist on main; it was picked up by a `git add -A`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(init): keep the workflow note off runs that generate nothing With no tools selected (or only tools that could not receive a surface), `openspec config profile` followed by `openspec update` writes nothing, so naming the missing workflows pointed at the wrong problem. Reported by CodeRabbit on this PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(init): drop the redundant update step from the workflow note `openspec config profile` offers to apply to the current project before it exits, and prints the `openspec update` guidance itself when the user declines, so naming a second command was one step too many. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(troubleshooting): match the profile steps to what the CLI does `openspec config profile` applies to the current project itself, so listing `openspec update` as a second required step was wrong; it is the fallback for declining the prompt or for other projects. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(update): name the workflows the profile left out `openspec update` is what the troubleshooting checklist tells a user to run when a command they read about never appeared, and it is what people run after upgrading the CLI. Neither of its existing profile notes fires on the default `core` profile, so that user reached "All tools up to date" and still learned nothing about the six workflows they don't have. The note is the fallback pointer: silent when the extra-workflow or missing-core note already named `openspec config profile`, and when no configured tool can receive a workflow surface under the active delivery. Reading the two existing notes as one short-circuited `||` would have swallowed whichever ran second; they are evaluated separately. Relates to #1076 (the optional-workflow discoverability half; the Windows command-discovery repro in that thread is not addressed here) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(update): gather the profile notes behind one call The two call sites had grown identical six-line blocks. One displayProfileNotes() keeps the ordering and the single-pointer rule in one place, where the "evaluate every note, never chain them with ||" constraint can be stated once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: drop the legacy troubleshooting entry alfred-openspec on #1779: docs-lab/README.md says the old docs/ tree is legacy, is no longer used by the site, and must stay untouched. The canonical docs-lab/customize/profiles.md already lists the six optional workflows and the 'openspec config profile' command that adds them, and the root README already calls out the expanded set, so this entry was a third copy in a stale tree. The docs-lab troubleshooting page is a heading-only skeleton held back from the site, so there is nothing to move it to; this PR is now source and tests only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6d2dbe62d3 |
fix(propose): load project context before planning (#1657)
* fix(propose): load project context before planning * test(propose): assert project context is applied * fix(propose): honor project context limits * fix(propose): fail closed on unsafe context * fix(propose): skip config without a root * chore(parity): regenerate hashes after merging main * fix(propose): harden early context loading guidance * fix(propose): require initialization before planning in bare repos --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1c0ee701e5 |
docs: add CONTRIBUTING.md (#1781)
* docs: add CONTRIBUTING.md Require a discussion (core design changes) or an issue before a PR is opened, and require every PR to link its issue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: add setup and PR steps to CONTRIBUTING.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: make CONTRIBUTING.md the single source for the process The README's Contributing section said small fixes could go straight to a PR, which contradicts the new discussion/issue requirement. Point it at CONTRIBUTING.md and carry over the conventional-commit and AI-disclosure policies so nothing is lost. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: close the three process gaps in CONTRIBUTING.md alfred-openspec on #1781: 1. The OpenSpec-proposal rule was dropped from the README with nothing replacing it, recreating the gap in #1727. New step 2 carries the threshold over verbatim from the README (new features, significant refactors, architectural changes) plus the philosophy paragraph, says to open the proposal as its own PR and wait for approval, and tells anyone unsure to ask in the issue from step 1. 2. The discussion path contradicted itself: step 1 accepted a prior discussion while step 3 required 'Closes #123'. The PR step now says to link what you opened in step 1, 'Closes #123' for an issue or a link to the discussion when there is no issue. CodeRabbit's thread on README.md:227 is the same defect, so the README sentence says 'the issue or discussion' too. 3. The local setup was missing 'pnpm exec tsc --noEmit', which CI runs, and the README called the guide a development setup after 'pnpm run dev' and 'dev:cli' were removed. The command is added, the guide states that those four commands are exactly what CI runs, and the README pointer now describes the guide as the full process rather than a setup. Verified each documented command against this checkout: build, tsc --noEmit and lint all pass as written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0b60a0ac1f |
chore(deps): bump the website-dependencies group in /website with 5 updates (#1815)
Applies dependabot's website bumps (#1812) and syncs the postcss override in website/pnpm-workspace.yaml, which dependabot does not know about, keeping the three override declarations in agreement. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6981c84df0 |
chore(deps): bump zod to 4.5.4 and eslint to 10.9.1 (#1814)
Consolidates the two open root-lockfile dependabot bumps (#1810, #1811) into one PR so the pinned flake.nix pnpmDeps hash only has to be regenerated once. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
63666c8bb2 |
ci: report the correct pnpmDeps hash when flake.nix is stale (#1817)
* ci: report the correct pnpmDeps hash when flake.nix is stale Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: scope the reported hash to the pnpmDeps block Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci(flake): scope every hash rewrite to the pnpmDeps block alfred-openspec on #1817: the workflow read is scoped now, but the script it runs is not. update-flake.sh read CURRENT_HASH from the first hash assignment anywhere in flake.nix, and all three in-place rewrites matched every hash assignment. flake.nix holds one fixed-output derivation today, so that lands on the right line by luck; add a second and the script stamps the placeholder over both, reads back whichever mismatch Nix reported first, and writes pnpmDeps' hash into the other derivation. Scoping only the workflow left that path fragile, as the review says. The address range is declared once as PNPM_DEPS_BLOCK and used by the read and all three rewrites, so the scoping cannot drift between call sites. Also guards the read: an unmatched block previously left CURRENT_HASH empty, and the failure path would then restore hash = "". It now exits before touching the file. Verified against a three-derivation fixture with pnpmDeps in the middle, which catches both shapes of the bug: the scoped read returns the pnpmDeps hash while an unscoped read returns the first derivation's, the placeholder is written once rather than three times, and the neighbouring hashes survive the restore. That fixture is the new test, alongside a static check that no hash read or rewrite in the script is missing the range. Verified the static check fails when any one call site is unscoped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(flake): run the scoping fixture on its own volume The new test failed on windows-pwsh with 'sed: cannot rename ./sedKaAflu: Invalid cross-device link'. sed -i writes its temp file in the working directory and renames it over the target; on a GitHub Windows runner the repo is on D: and os.tmpdir() is on C:, so that rename crosses volumes. bash now runs with cwd set to the fixture directory and addresses the file by name, which keeps the temp file and its rename on one volume. The assertions are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e062b9572b |
Version Packages (#1766)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>@fission-ai/openspec@1.12.0 v1.12.0 |
||
|
|
fbd4160b37 |
docs: reroute unfinished store reference links (#1767)
Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
9ec0a090b8 | fix(security): patch fast-uri advisories (#1768) | ||
|
|
9dfffd87b3 |
chore(deps): bump the website-dependencies group across 1 directory with 7 updates (#1765)
* chore(deps): bump the website-dependencies group across 1 directory with 7 updates Bumps the website-dependencies group with 7 updates in the /website directory: | Package | From | To | | --- | --- | --- | | [fumadocs-core](https://github.com/fuma-nama/fumadocs) | `16.14.5` | `16.15.2` | | [fumadocs-mdx](https://github.com/fuma-nama/fumadocs) | `15.2.3` | `15.3.1` | | [fumadocs-ui](https://github.com/fuma-nama/fumadocs) | `16.14.5` | `16.15.2` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.31.0` | `1.34.0` | | [next](https://github.com/vercel/next.js) | `16.3.1` | `16.3.3` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.2.0` | `26.3.0` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.2.4` | `19.2.5` | Updates `fumadocs-core` from 16.14.5 to 16.15.2 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.14.5...fumadocs@16.15.2) Updates `fumadocs-mdx` from 15.2.3 to 15.3.1 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs-mdx@15.2.3...fumadocs-mdx@15.3.1) Updates `fumadocs-ui` from 16.14.5 to 16.15.2 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.14.5...fumadocs@16.15.2) Updates `lucide-react` from 1.31.0 to 1.34.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.34.0/packages/lucide-react) Updates `next` from 16.3.1 to 16.3.3 - [Release notes](https://github.com/vercel/next.js/releases) - [Commits](https://github.com/vercel/next.js/compare/v16.3.1...v16.3.3) Updates `@types/node` from 26.2.0 to 26.3.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react-dom` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: fumadocs-core dependency-version: 16.15.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-dependencies - dependency-name: fumadocs-mdx dependency-version: 15.3.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-dependencies - dependency-name: fumadocs-ui dependency-version: 16.15.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-dependencies - dependency-name: lucide-react dependency-version: 1.34.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: website-dependencies - dependency-name: next dependency-version: 16.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: website-dependencies - dependency-name: "@types/node" dependency-version: 26.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: website-dependencies - dependency-name: "@types/react-dom" dependency-version: 19.2.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: website-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> * fix(website): align esbuild build approval --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
cb5ae2cd16 |
ci: bump changesets/action from 1.9.0 to 2.1.1 in the github-actions group (#1746)
* ci: bump changesets/action in the github-actions group Bumps the github-actions group with 1 update: [changesets/action](https://github.com/changesets/action). Updates `changesets/action` from 1.9.0 to 2.1.1 - [Release notes](https://github.com/changesets/action/releases) - [Changelog](https://github.com/changesets/action/blob/main/CHANGELOG.md) - [Commits](https://github.com/changesets/action/compare/a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d...8488615a623b1b9c987934bb89eae8af6a946ac1) --- updated-dependencies: - dependency-name: changesets/action dependency-version: 2.1.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> * fix(ci): complete changesets action v2 migration * fix(nix): invalidate pnpm dependency hash * fix(nix): use calculated dependency hash * fix(nix): refresh pnpm dependency hash --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
db03c6c4b0 |
feat(validate): add findings-only bulk reports (#1713)
* feat(validate): propose findings report * docs(validate): clarify findings report contract * feat(validate): implement and harden bulk findings reports * test(validate): canonicalize store paths natively --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
a4fcdbece6 |
feat(validate): report the deltas archive would refuse (#1710)
* feat(validate): report the deltas archive would refuse validate checked a change's deltas against themselves and, for MODIFIED blocks, against the main spec's scenarios. It never checked whether the main spec can supply the target a delta acts on, so a MODIFIED naming a requirement that is not there, a RENAMED whose source is gone, or an ADDED whose name already exists all validated clean and failed at archive instead - typically weeks later, after the implementing PR had shipped and the authoring session was gone. Run the merge archive runs and report what it refuses. buildUpdatedSpec returns the rebuilt content without writing it, so the preflight is the same function on the same inputs with the result discarded, and cannot disagree with the code that does the writing. That matters here: several of those preconditions deliberately read a missing target as already-synced rather than as a failure, and a second copy of the rules would be free to drift. Reported as INFO so no verdict changes in any mode. A MODIFIED whose target is missing is also what a change modifying a sibling's unarchived requirement looks like, and validate stays valid for that case today; telling the two apart needs the opt-in marker #1112 asks for. What is missing until then is the information, not the verdict. Refs #1112 * fix(validate): skip preflight for deltas whose errors come after the loop missingHeaderSpecs and emptySectionSpecs are collected inside the per-spec loop but only become issues after it, so a suppression set built from the issues raised so far could not see them. A headerless or empty-section delta has nothing for the merge to apply, so the preflight reported that as a blocker of its own, on top of the error that names the actual mistake. * fix(validate): harden archive preflight diagnostics * fix(validate): preserve reports when archive preflight cannot start --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
0296401b82 |
fix(init): add .gitkeep files to empty directories (#786)
* fix(init): add .gitkeep files to empty directories After running openspec init, the specs/, changes/, and changes/archive/ directories are empty. Since git does not track empty directories, these folders are lost when the repository is cloned, causing openspec list to recommend re-initialization. Added .gitkeep file creation to createDirectoryStructure() for both normal and extend modes, ensuring empty directories are preserved in version control. Fixes #269 * fix(init): preserve directory anchors without overwriting user files --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
cd724449ac |
refactor(core): share the IDE restart hint between init and update (#1725)
* refactor(core): share the IDE restart hint between init and update
init named the surface it generated ("the new commands" / "the new skills");
update printed a generic "changes" for the same event, decided by the same
rule. Extract that rule and its wording into shared/ide-restart.ts so both
commands say the same thing, and update now names the surface too.
No condition changed: the hint still requires one tool that is both
IDE-resident and actually received a generated surface under the active
delivery, so a CLI tool's commands can never speak for an IDE tool that got
nothing.
Verified by mutation: dropping the IDE-resident filter turns 9 tests red
across the helper, init and update; swapping the commands/skills precedence
turns 5 red.
* test(core): harden shared IDE restart guidance
* fix(core): describe restart guidance for removed workflows
---------
Co-authored-by: Clay Good <hi@claygood.com>
|
||
|
|
954d4796a4 |
docs(community): add a community showcase (#1739)
* docs(readme): list the independent openspec ui project * docs(community): move the showcase out of the readme |
||
|
|
98bf53e59e | fix(workflows): ground proposals in relevant project code (#1737) | ||
|
|
2fd175c8b0 |
docs(cli): document managed PowerShell completion setup (#1070)
* docs(cli): add Windows PowerShell completion example The shell completion documentation only showed Unix/bash examples, making it unusable for Windows users. Added platform-specific examples for both Unix/macOS (bash) and Windows (PowerShell). Changes: - Add Unix/macOS (bash) example with ~/.bash_completion.d path - Add Windows (PowerShell) example with C:\Users\y00031947\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 path - Improve clarity with platform labels Fixes: Windows users cannot use shell completion manual installation * fix(cli): use append operator for PowerShell profile to avoid data loss Critical fix: Using '>' operator would overwrite the user's PowerShell profile, deleting existing configurations. Changed to '>>' to append instead of overwrite, preserving user's existing settings. * docs(cli): harden PowerShell completion setup --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
b976106d95 |
fix(explore): guide planning with focused discovery questions (#1017)
* feat: improve explore discovery questions * chore: add changeset for explore guidance --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
cdd06a0594 |
docs(specs): align four requirements with current behavior (#1707)
* docs(openspec): correct 4 requirements that the code has outgrown - ai-tool-paths/path-configuration-for-supported-tools#2: Changed the windsurf scenario's required skillsDir from `.windsurf` to `.devin`. - cli-update/slash-command-updates#6: Require $ARGUMENTS to be placed in the file body (not frontmatter) for OpenCode archive commands. - rules-injection/validate-artifact-ids-during-instruction-loading#6: Updated the expected warning text to use double quotes and to state it matches no artifact in any available schema, listing known artifact IDs. - specs-sync-skill/skill-output#3: Changed the expected no-changes message to 'Specs already in sync; no files changed.' to match the code. None of these reduce what a requirement demands. Scanned at |
||
|
|
1bcdf1b032 |
fix(build): prepare npm git installs without pnpm (#792)
* fix: handle npm git dep installation for GitHub installs npm v11's git dep preparation runs `prepare` before node_modules exist in the temp clone directory, causing TypeScript compilation to fail. Changes: - build.js: skip build gracefully when node_modules absent - package.json: use `node build.js` directly in prepare/prepack for npm compatibility (avoids pnpm dependency during git dep install) Note: postinstall.js already handles all errors internally via main().catch(() => process.exit(0)), so no `|| true` wrapper needed. Install from GitHub with: npm pack github:user/repo#branch npm install -g ./fission-ai-openspec-x.y.z.tgz Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(build): prepare npm git installs without pnpm --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
44a39eb24b |
feat(core): add codeassistant support (#1171)
* feat(core): add codeassistant support Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: change format file and add test Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: add tests Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * fix: escaped description yaml values Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: add test Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: change adapter Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: handle \r in description Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore: use common escapeYamlValue helper Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> * chore(release): track sourcecraft support --------- Signed-off-by: Александр Мелентьев <aleksandr4842@ya.ru> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
142b8a9203 |
fix(docs): route OpenSpec pixel through Pages (#1757)
* Route /openspec-pixel.svg to the docs Pages deployment The docs nav logo is referenced via the root-relative path /openspec-pixel.svg, which isn't matched by isDocsRoute() and so falls through to the Astro landing site instead of the docs Pages project that actually has the asset - a 404. /icon.svg already has this exact special case; this adds the same for the pixel logo. Fixes #1756 * fix(docs): route pixel logo through worker --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
6911f55175 |
fix(deps): preserve Node 20 chalk compatibility (#1747)
* chore(deps): bump chalk from 5.6.2 to 6.0.0 Bumps [chalk](https://github.com/chalk/chalk) from 5.6.2 to 6.0.0. - [Release notes](https://github.com/chalk/chalk/releases) - [Commits](https://github.com/chalk/chalk/compare/v5.6.2...v6.0.0) --- updated-dependencies: - dependency-name: chalk dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore(nix): refresh dependency hash * fix(nix): use calculated dependency hash * fix(deps): preserve Node 20 chalk compatibility --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
c5c38a7aca |
chore(deps-dev): bump eslint from 10.8.1 to 10.9.0 in the development-dependencies group (#1745)
* chore(deps-dev): bump eslint in the development-dependencies group Bumps the development-dependencies group with 1 update: [eslint](https://github.com/eslint/eslint). Updates `eslint` from 10.8.1 to 10.9.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.8.1...v10.9.0) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.9.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> * chore(nix): refresh dependency hash * fix(nix): use calculated dependency hash --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
d0071d7326 | docs(archive): show how to retire capabilities (#1751) | ||
|
|
a0ddb60d04 |
Version Packages (#1728)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v1.11.0 |
||
|
|
2fa679f180 |
fix(schema): make schema init --default actually set the default (#1709)
* fix(schema): make schema init --default actually set the default --default wrote defaultSchema to openspec/config.yaml, but the config loader only reads schema, so new changes kept using spec-driven while the command reported success. Write the key that is read, and drop the dead one a previous run may have left behind. Fixes #1708 * fix(schema): preserve supported configs when setting default * docs(schema): specify default config file handling * docs(schema): protect default config migration * fix(schema): make default initialization atomic * fix(schema): hide init staging and backup dirs from discovery `schema init` stages into `.init-staging-<rand>` and moves an existing schema aside to `<name>.init-backup-<pid>-<ts>`, both inside the schemas dir. `schema fork` already did this and the resolver filters its temp names out of discovery; the init names were never added, so `listSchemas` and `listSchemasWithInfo` surfaced them as real schemas -- in shell completions, "available schemas" error lists, and change-metadata validation. The backup is the durable case: cleanup failure is deliberately tolerated with a warning, so a blocked cleanup (or a crash mid-transaction) leaves a permanent phantom schema behind. Generalize the fork-only filter to cover both commands' staging and backup names. Real schema names are kebab-case, so excluding these dot-bearing names can never hide a legitimate schema. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e5e350d04b |
fix(explore): use ASCII in diagram examples (#1010)
* fix: Use ASCII arrows instead of unicode This fixes the issue of ambiguous unicode character width when visualizing on terminals * fix: Update remaining docs within explore to use ASCII * fix(explore): finish the ASCII conversion and guard it Rebase onto main and close the gaps in the original fix: - Regenerate skills/openspec-explore/SKILL.md. The static skills/ mirror landed after this branch was cut, so the parity test would have failed with the template and the mirror out of sync. - Regenerate the three parity hashes through scripts/regen-parity-hashes.mjs. - Convert the ambiguous-width glyphs the first pass missed: the bullets in the CLI-storage example, and the check/cross marks in its comparison table, which sat in the column-aligned block the bug is about. - Tighten the ASCII guidance to two lines. It ships into every user project on both delivery surfaces, so the paragraph was pure overhead. - Add regression tests (#983): every fenced example in both the skill and the command body must be free of box-drawing, arrow, bullet, and check/cross glyphs, and the guidance must state the rule and the reason. - Add a patch changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(explore): cover every check/cross dingbat in the ASCII guard The matcher listed U+2713 and U+2717 only, so a fenced example could use ✕ (U+2715) or ✘ (U+2718) — same ambiguous width, same misalignment — and still pass. Widen to the U+2713-U+2718 run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(explore): require explicit confirmation before writing files * test(explore): harden write confirmation guardrail * fix(explore): scope write confirmation precisely --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ayman D. <ayman.bacc@gmail.com> |
||
|
|
dd7cea3ffe |
feat(show): diff delta requirements against the main specs (#980)
* Proposed feature: openspec show --diff to see changed requirements more clearly * Implementation of the --diff feature, which led to some spec changes during implementation. * Update the proposal to be clearer. Thanks coderabbit * Fix a bug identified by coderabbit with excessive trimming, and add a testcase for it. * fix(show): harden --diff for review feedback Keeps `openspec show <change>` without `--diff` a raw proposal passthrough, reports when a change has no delta specs instead of returning silently, preserves the Reason/Migration body of a REMOVED requirement, and resolves main specs through the command's root so `--store <id>` diffs against that store. Text mode and JSON mode now render from one shared collection pass, the CLI tests drive argv arrays from a mkdtemp project instead of interpolated shell strings, `--diff` is registered for shell completions, and the stray package-lock.json is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * build(deps): declare the diff dependency and refresh the flake hash Adds the `diff` runtime dependency that requirement-diff.ts imports, updates pnpm-lock.yaml, and regenerates the flake's pnpmDeps hash so `nix build` matches the new lockfile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(show): diff nested capabilities too collectSpecDiffs enumerated only top-level directories under the change's specs/, so a nested capability (specs/<area>/<id>/spec.md) was skipped: text mode printed nothing for it and its MODIFIED deltas came back from --json with no diff. It now uses the same discoverSpecFiles() helper ChangeParser uses, so the capability ids match the `spec` field of the JSON deltas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(show): report header mismatches instead of hiding them Two cases where --diff quietly showed something misleading: A MODIFIED requirement whose capability has no main spec was rendered as all-additions, which reads like a new capability. It is an authoring error archive will reject, so it now prints the raw text with a warning naming the missing spec. A header that differs from the main spec only in case or interior spacing found no match at all under exact lookup, or matched under a lowercase-only comparison that let a real mismatch through silently. Lookup is now exact first, then the shared foldRequirementName fallback, and a folded match prints the diff the author meant alongside a warning that archive matches names exactly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(show): name the main spec as such in collectSpecDiffs Comment and locals still called the main spec the "base" spec, and the no-main-spec comment described the old all-additions behavior. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps-dev): bump the development-dependencies group with 2 updates Bumps the development-dependencies group with 2 updates: [smol-toml](https://github.com/squirrelchat/smol-toml) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `smol-toml` from 1.7.1 to 1.8.0 - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.7.1...v1.8.0) Updates `typescript-eslint` from 8.66.0 to 8.67.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: smol-toml dependency-version: 1.8.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: typescript-eslint dependency-version: 8.67.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> * chore(nix): invalidate pnpm dependency hash * fix(nix): update pnpm dependency hash * fix(show): harden requirement diff output * build(nix): pin combined dependency hash * test(show): assert proposal precedes diffs * docs(show): clarify JSON diff diagnostics * fix(show): retain unified diff hunk headers --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
ab81a4b43a |
fix(completions): stop Fish completions falling back to filenames (#1199)
* fix(completions): suppress filesystem fallback in Fish completions * fix(completions): force files back on for path positionals in Fish Fish never restores filesystem completion once a matching rule sets --no-files, so a path positional needs an explicit --force-files rule. Without it, `openspec store register <TAB>` lost file completion because the sibling subcommand rules in the same context now carry -f. Also drop retired "context store" vocabulary from the test fixtures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(completions): preserve workset member paths in fish * fix(completions): force Fish path fallback * fix(completions): harden Fish option handling * fix(completions): scope Fish path fallback * fix(completions): match Fish command paths exactly * fix(completions): skip parent options in Fish paths --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
109f81f17d |
fix(antigravity): update skill and workflow paths from .agent to .agents (#830)
* fix(antigravity): update skill and workflow paths from .agent to .agents Antigravity has migrated from .agent (singular) to .agents (plural) for workspace skills and workflows. The old .agent path still works via backward compatibility, but the official docs now specify .agents. Changes: - config.ts: skillsDir '.agent' -> '.agents' - antigravity adapter: workflow path '.agent/workflows/' -> '.agents/workflows/' - legacy-cleanup: add patterns for old .agent/ artifacts cleanup - docs: update supported-tools.md table - tests: update expected path assertions Closes #0 (reported by BugsCreator and Minh Pham in Discord) * fix(antigravity): migrate an existing .agent install to .agents Pointing Antigravity at `.agents` leaves every existing `.agent/` install behind, so this registers the move instead of only changing the target: - `.agent` becomes Antigravity's legacy skills root and legacy tool root, so update relocates managed skills and commands after generating their replacement, keeping a file the user customized. - Detection keys off `.agent` and `.agents/workflows`. The bare `.agents` root is shared with Codex, Zed, and the vendor-neutral target, so it cannot stand in for "Antigravity is set up here". - Delivery inference reads a tool's legacy roots for command files too. Without it the first update after the move saw skills but no commands, wrote `delivery: skills`, and the next update deleted every slash command. - Legacy slash-command cleanup stays scoped to the pre-opsx `openspec-*` names under `.agent`; the migration owns the `opsx-*` files, and a shared root is never glob-swept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: record the Antigravity root move Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(antigravity): require a generated command before moving its legacy copy migrateCommandFiles relocated a legacy command file whether or not the current root held a replacement. Codex never reached that path — it has no command adapter — so Antigravity is the first after-generation move where it matters: under skills-only delivery, or for a deselected workflow, the move recreated a command OpenSpec had just decided not to install. Gate the move on an existing destination, the same way migrateSkillDirs already does for after-generation timing. Reported by CodeRabbit on #830. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(antigravity): arbitrate shared agent skills * test(antigravity): cover Windows migration paths * fix(antigravity): harden shared-root migration --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
04b37ac1d5 |
fix(archive): preserve requirement order when renaming (#1712)
* fix(archive): preserve requirement order when renaming * chore(archive): add rename-order changeset --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
126c5d6c59 |
fix(validate): report a Purpose left as the archive placeholder (#1671)
* docs(openspec): propose warn-on-purpose-placeholder
When a delta introduces a capability with no usable `## Purpose`, archive
writes `TBD - created by archiving change <name>. Update Purpose after
archive.` into the new main spec. Three places already tell authors to
replace it -- the `specs` instruction ("including a leftover `TBD`
placeholder"), the sync-specs summary step ("so it gets written now rather
than lingering"), and the cli-archive contract -- but nothing reports that
it is still there.
`--strict` cannot reach it. The check meant to catch a Purpose nobody wrote
is a 50-character floor and the placeholder is 91 characters, so the one
rule that exists to catch a thin Purpose is satisfied by the exact text
meaning nobody wrote one: a Purpose reading "Does stuff." fails --strict
today, while one saying nothing at all passes.
Proposes reporting it as a warning against the spec's Purpose -- silent by
default, failing under --strict, so a project already carrying placeholders
keeps validating until it opts into the stricter gate. Detection is narrow:
the generated sentence wherever it appears, and otherwise only a `TBD`
opening the Purpose, so prose raising an open question is left alone.
Planning artifacts only; no source changes.
Refs #369
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(validate): report a Purpose left as the archive placeholder
When a delta introduces a capability with no usable `## Purpose`, archive
writes a placeholder into the new main spec. Nothing read it afterwards, so
the capability kept a to-do in it while every command reported success.
`--strict` could not reach it. The check that exists to catch a Purpose
nobody wrote is a 50-character floor, and the placeholder clears it: a spec
whose Purpose read "Does stuff." failed --strict, while a spec whose Purpose
said nothing at all passed. #369 reported agents leaving the placeholder
behind and stayed open seven months; every remedy since has been an
instruction, which is the mechanism that report described as unreliable.
validate now reports it as a warning against the Purpose, naming the line
and saying to edit the main spec directly -- a delta's `## Purpose` is read
only when the capability is created, so it cannot replace an existing one.
Warning rather than error, because strict mode already means "warnings
fail": a project carrying placeholders keeps validating by default and only
--strict fails. Archive is untouched -- it validates rebuilt specs without
--strict, so a spec archive writes still passes the validation it would have
passed before, and the text archive writes is byte-identical.
The placeholder is recognised through the same constants the writer composes
it from, so the check cannot drift from the sentence it looks for -- the
failure mode of a second, hand-copied spelling being a check that matches
nothing and looks exactly like a check that found nothing. The one case that
cannot be a lookup is an agent-written placeholder, kept to a `TBD` opening
the Purpose: "the retry budget is TBD pending benchmarks" is authored prose
and is left alone.
Verified: 209 archive tests pass unchanged (the placeholder text is
asserted literally, so the output is provably identical); full suite 138
files / 3993 tests; 36/36 strict spec validations; build, lint and typecheck
clean. Against a project carrying four real placeholders, default mode still
exits 0 and --strict fails exactly those four.
Cross-platform CI is not yet confirmed -- it needs a pushed branch. Line
endings are covered by tests asserting a CRLF spec and an LF spec produce
identical findings, and the module does no path handling.
Refs #369
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(validate): make every placeholder guard load-bearing
A mutation pass over the seven guards -- revert one, see which tests die --
found two that no test held.
The prefix/suffix test did not exercise the guard it named. Its Purpose read
"Explains what happens when archiving change my-change runs twice", which
contains neither half of the generated sentence, so it passed whether or not
the suffix was required. Matching on the prefix alone killed nothing. The
Purpose now embeds the real prefix constant and asserts the suffix is absent,
so the case is the one the name claims; the mutation kills it.
The empty-Purpose early return was genuinely dead. Neither rule matches empty
text, so removing the branch changed no behaviour and failed no test. Rather
than keep a guard nothing can hold, the branch is gone and the comment says
why an empty Purpose still yields null. The tests asserting that behaviour
are unchanged and still pass.
Every guard now dies under mutation:
whole check removed from applySpecRules ......... 6 tests
brevity no longer suppressed (else -> if) ....... 1
word boundary dropped from the TBD marker ....... 1
generated placeholder matched on prefix alone ... 1
line-ending normalisation removed ............... 2
section-boundary guard removed from locator ..... 1
Full suite 138 files / 3993 tests, lint and typecheck clean.
Refs #1670
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(openspec): record the mutation pass in the task list
The mutation work changed the implementation -- a test rewritten and a dead
branch removed -- but no task covered it, so the plan claimed less work than
was done. Added as group 6, marked complete, with why it was not planned.
5.4 now says what blocks it. It needs a pushed branch for the cross-platform
matrix, and the note records that line endings are covered locally by tests
asserting a CRLF spec and an LF spec produce identical findings, so a reader
can tell the difference between unverified and unverifiable-from-here.
The specs, proposal and design are unchanged and were checked: the delta's
empty-Purpose clause constrains behaviour, not structure, and that behaviour
is the same -- the redundant branch went, the rule did not.
26 of 27 tasks complete; the change still validates --strict.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(openspec): close 5.4 on a green cross-platform matrix
CI dispatched on the fork against this branch: lint & typecheck, and the test
suite on linux-bash, macos-bash and windows-pwsh -- all green. The Windows job
installed, built and ran the suite rather than short-circuiting, which is the
part 5.4 existed to check, since the placeholder locator counts lines in files
that may carry either ending.
Recorded as a workflow_dispatch run on the fork, not the upstream pull-request
run, because those are not the same gate and the note should not let a reader
assume otherwise. Nix Flake Validation and Validate Release Tracking skipped:
this branch touches neither the flake nor release tracking.
27 of 27 tasks complete.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(validate): name the placeholder's line, not the prose above it
The warning tells you which line to fix, and named the wrong one when the
generated sentence did not open the Purpose:
3 ## Purpose
4 Handles widget retries. <- warning pointed here
5
6 TBD - created by archiving ... <- placeholder is here
The locator asked "what is the first non-blank line after ## Purpose?"
rather than "where is the placeholder?". Those are the same line in five of
the six shapes a placeholder can take -- a leading TBD marker is the first
non-blank line by definition, and archive writes the generated sentence as
the section's only content -- so the two questions only diverge when a human
types prose above a leftover placeholder.
Pointing at that prose is worse than pointing nowhere: the reader sees a
sentence that is plainly fine and concludes the check is broken. design.md
already said a wrong line number is worse than none, and the delta already
required naming the line the placeholder is on, so this is the
implementation meeting a contract that was already written, not a change of
contract.
The locator is now told which rule matched. A leading marker keeps the
first-non-blank behaviour, because that is where it sits; the generated
sentence is located by its own text. When both match the leading marker
wins, being the earlier of the two.
Found by CodeRabbit on #1671. The finding was real despite its own
"Addressed" marker, which only tracked the file changing in a later commit.
Two test gaps let it through. The case that covered this input asserted
only that something was reported, never which line -- so it now asserts the
line, and a table pins every position a placeholder can occupy, each case
first checking that the line it expects really carries the placeholder. The
mutation pass could not have caught it either: mutation proves a test dies
when a guard is broken, and cannot invent an assertion nobody wrote.
Reverting the branch fails exactly the three new expectations. Full suite
4000 tests / 138 files, lint and typecheck clean.
Refs #1670
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(validate): set the placeholder line unconditionally
ValidationIssue.line is optional and the project does not enable
exactOptionalPropertyTypes, so a plain assignment typechecks and
JSON output is unchanged - JSON.stringify drops undefined values.
findPurposePlaceholderIssue already returns the key unconditionally,
and the neighbouring push sites assign line plainly, so the
conditional spread was the odd one out.
* fix(validate): widen the placeholder check to TODO and read fences as quoted
#1670 left two questions open. Both are answered here, against how OpenSpec
already reads a spec.
A `TODO` opening the Purpose now reports as the same finding as a `TBD`.
Nothing OpenSpec writes produces one, but the marker an author leaves behind is
whichever word they reached for, and a Purpose reading `TODO: fill this in` is
as unwritten as one reading `TBD`. Only the opening position counts, as before,
so `TODOs are tracked in the linked issue` is still authored prose.
Fenced code inside a Purpose is now read as quoted material rather than as the
Purpose speaking, through the `buildCodeFenceMask` the requirement and structure
parsers already share. Without it a spec documenting the sentence archive writes
is reported as carrying it, which is the check failing the one document that
explains it - and a warning that fires on the docs teaches people to ignore the
warning. Fenced lines are skipped when locating the placeholder too, so a
`## Purpose` or `## Requirements` quoted in a fence can neither be mistaken for
the section header nor end the section early.
The message now names both what archive writes and a marker left in its place,
since one message covers both. Severity is unchanged: still a warning, so a
project carrying placeholders keeps validating and only --strict fails.
Every new guard is mutation-checked: dropping `TODO` kills 3 tests, unmasking
detection kills 2, unmasking the line locator kills 3, unmasking the header
search kills 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(validate): read the marker boundary in any script, not just ASCII
Review found `\b` reading `TODOé` and `TBD١` as a marker followed by
punctuation, because `\b` only knows ASCII word characters. A Purpose is prose
and prose is not always Latin script, so the rule that a longer word beginning
with those letters is not a marker has to hold in any script.
The lookahead rejects letters, digits, combining marks and `_`, and nothing
else, so `TODO:`, `TBD -` and `TODO(owner):` are still the marker they look
like. Held in both directions: loosening it back to `\b` kills 1 test,
tightening it to reject punctuation kills 4.
Also reworded a task line that opened with `#1670`, which markdownlint reads as
a heading missing its space.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(validate): locate the matched purpose placeholder
* docs(validate): remove trailing task whitespace
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Clay Good <hi@claygood.com>
|
||
|
|
a7353aea9a |
feat(status): add --all for batch status of every active change (#1301)
* feat(status): add --all for batch status of every active change
`openspec status --all --json` reports every active change in one
process instead of one CLI spawn (~500ms module-load) per change,
mirroring the existing `validate --all`. Emits a single
`{ changes: [ChangeStatus, ...], root }` envelope sorted by change
name; a change that fails to load contributes a per-change error entry
instead of failing the sweep. `--all` and `--change` are mutually
exclusive, honoring the --json null-shape on failure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(status): harden --all per adversarial review findings
- Validate --schema before the no-changes early return so a bogus
schema fails consistently whether or not any change exists.
- Text mode now exits 1 when any change fails to load (mirrors
validate --all); JSON mode still exits 0 with per-change diagnostics.
- Add tests for the --all --schema interaction (unknown schema
null-shape, override propagation, broken-metadata precedence) and
text-mode failure rendering.
- Changeset heading to "### New Features" per repo convention; add
status --all to the agent quick-reference table in docs/cli.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(status): thread batch null-shape through root resolution, align sort with validate
Code-review findings on --all:
- Pass failurePayload: { changes: [] } to resolveRootForCommand so a
root-selection failure under --all --json still emits the documented
batch null-shape (siblings like list/doctor/context already do this).
- Sort with localeCompare to match validate --all's ordering for
mixed-case change names.
- Extract a shared loadStatus helper so the batch and single-change
payloads cannot drift apart.
- Changeset no longer claims exact validate --all parity (JSON exit
semantics deliberately differ).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(status): give the --all failure null-shape a single owner
Simplify pass on the --all diff: hoist the { changes: [] } batch
null-shape into an exported BATCH_STATUS_FAILURE_PAYLOAD constant so
the root-resolution and CLI-wrapper failure paths cannot drift, replace
the conditional spread with the plain ternary the sibling call site
already uses, drop a redundant array copy before sort, and narrow the
text-mode failure counter to the boolean it actually is.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(status): point the missing-target error at --all, correct the docs
`openspec status` with neither --change nor --all listed the available
changes and named only --change, so the batch path was discoverable
only from --help. The error now offers both.
Also corrects two stale claims in the status section of docs/cli.md
that the new row sits next to: the command never prompts for a change
(it errors), and bare `openspec status` is not an interactive check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(status): assert --all carries store context like the single-change path
The batch sweep resolves the root once and threads the store id into
every entry. Nothing pinned that: a regression would have shown up only
as a wrong path inside an agent's JSON. Assert the sweep's envelope root
and per-change payload match `status --change` in a registered store.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(status): fail incomplete batch reports
* docs(status): clarify empty and batch output
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Clay Good <hi@claygood.com>
|
||
|
|
c0c50f9a4c |
chore(deps-dev): bump the development-dependencies group with 2 updates (#1718)
* chore(deps-dev): bump the development-dependencies group with 2 updates Bumps the development-dependencies group with 2 updates: [smol-toml](https://github.com/squirrelchat/smol-toml) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `smol-toml` from 1.7.1 to 1.8.0 - [Release notes](https://github.com/squirrelchat/smol-toml/releases) - [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.7.1...v1.8.0) Updates `typescript-eslint` from 8.66.0 to 8.67.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.67.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: smol-toml dependency-version: 1.8.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies - dependency-name: typescript-eslint dependency-version: 8.67.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> * chore(nix): invalidate pnpm dependency hash * fix(nix): update pnpm dependency hash --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
7010e26890 |
fix(explore): require explicit confirmation before writing files (#1716)
* fix(explore): require explicit confirmation before writing files * test(explore): harden write confirmation guardrail * fix(explore): scope write confirmation precisely --------- Co-authored-by: Clay Good <hi@claygood.com> |
||
|
|
6926ccb18a |
chore(deps): bump the website-dependencies group (#1719)
Bumps the website-dependencies group in /website with 2 updates: [fumadocs-core](https://github.com/fuma-nama/fumadocs) and [fumadocs-ui](https://github.com/fuma-nama/fumadocs). Updates `fumadocs-core` from 16.14.4 to 16.14.5 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.14.4...fumadocs@16.14.5) Updates `fumadocs-ui` from 16.14.4 to 16.14.5 - [Release notes](https://github.com/fuma-nama/fumadocs/releases) - [Commits](https://github.com/fuma-nama/fumadocs/compare/fumadocs@16.14.4...fumadocs@16.14.5) --- updated-dependencies: - dependency-name: fumadocs-core dependency-version: 16.14.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: website-dependencies - dependency-name: fumadocs-ui dependency-version: 16.14.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: website-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
f1b521dffa |
docs: rebuild docs site from docs-lab (#1649)
* docs: rebuild docs site from docs-lab
Replace the docs site's source tree with docs-lab, a page-by-page rebuild
of the OpenSpec docs (40 pages: Start / Guides / Customize / Multi-repo /
Reference / Help).
- Point website/docs.sync.config.mjs at ../docs-lab and restructure the
sidebar into nested groups; sync script gains nested meta.json emission,
leading-quote descriptions, idempotent writes, and diagram asset copying
- Remove the marketing landing page; / now redirects to /docs
(meta-refresh page + Cloudflare _redirects)
- Add remark plugins (faq, file-steps, gfm-alert) and the FileSteps
component backing the new page formats
- Add install.md at the repo root, curled by docs-lab/start/installation.md
as an agent-executable install prompt
- Add the docs authoring skills (.agents/skills/{write,draft,verify}-
openspec-docs); docs-lab/README.md links into write-openspec-docs
The old docs/ tree is now unused by the site and left for a follow-up.
Claude-Session: https://claude.ai/code/session_01BMMLYNJQPKXx1QHpnDn4ho
* docs: hold back unwritten pages, add worksets, drop diagram drafts
- website: comment out Overview, Guides, Architecture, Help, Legacy in
docs.sync.config.mjs until those pages are written; temporary
/docs -> /docs/installation redirect (Cloudflare _redirects + static
export meta-refresh fallback in page.tsx)
- docs-lab: new multi-repo/worksets.md page, published under Multi-repo
- docs-lab: content revisions across start/, customize/, reference/,
help/, multi-repo/; add review notes (Notes.md)
- remove docs-lab/diagrams option-* drafts and their website copies
- write-openspec-docs skill: add spoken-flow sentence rule
* docs: address review on PR #1649
- sync-docs: read the existing output directly instead of exists-then-read
(CodeQL TOCTOU alert)
- hold back the headings-only Environment variables and Stores reference
pages until written; links to them fall back to their GitHub source
- sources.md: cutover keeps docs/ in place and points at public/_redirects
- setup.md: label the workflow tree as the default set plus two optional ones
* docs: two review nits (spoken-flow rule, XDG_DATA_HOME note)
|
||
|
|
1ebddd17f4 |
Version Packages (#1705)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v1.10.0 |
||
|
|
7da3f34fb6 |
fix(tasks): include verification in generated plans (#1660)
* fix(tasks): include verification in generated plans * test(tasks): enforce inline verification guidance * fix(tasks): harden verification guidance * test(tasks): verify every onboarding checkbox |