mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-04 06:18:24 +08:00
Compare commits
80
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2500d6da97 | ||
|
|
bfa670eda9 | ||
|
|
760584ba9a | ||
|
|
ba0f508763 | ||
|
|
7056a58056 | ||
|
|
3a34ea309d | ||
|
|
94ca9c1eb1 | ||
|
|
81c2f9fce3 | ||
|
|
cd4f9e4a5f | ||
|
|
cf2859a520 | ||
|
|
c879d13d5f | ||
|
|
e232080d09 | ||
|
|
781c7f9447 | ||
|
|
d1642cb58c | ||
|
|
a7f08b8a46 | ||
|
|
f197804a38 | ||
|
|
ded99e27de | ||
|
|
772819417a | ||
|
|
405d8b51ed | ||
|
|
e923d05c8f | ||
|
|
de4141f0fd | ||
|
|
ee3ca3821e | ||
|
|
56528ea454 | ||
|
|
3de7c72c26 | ||
|
|
297092cb25 | ||
|
|
c21d897261 | ||
|
|
070de01dfa | ||
|
|
5a360c2088 | ||
|
|
3c3e6e3d42 | ||
|
|
486cfeb8ca | ||
|
|
f7d426ab9d | ||
|
|
817cdb64be | ||
|
|
88692b3bb4 | ||
|
|
5fe58590ba | ||
|
|
9557b43aaf | ||
|
|
d28fb49c1c | ||
|
|
bda85565ef | ||
|
|
28f864351b | ||
|
|
9a40b58928 | ||
|
|
baad4494b4 | ||
|
|
e70dcc7c82 | ||
|
|
187298289d | ||
|
|
42671df890 | ||
|
|
e7a9512d7c | ||
|
|
fffe3d3850 | ||
|
|
0ff63dbae6 | ||
|
|
d4e1c77eba | ||
|
|
79b6aa9c98 | ||
|
|
db23097835 | ||
|
|
7ac58dc790 | ||
|
|
336414665f | ||
|
|
072de6bc39 | ||
|
|
d6bdef6577 | ||
|
|
fb1b87613b | ||
|
|
f2812f6d18 | ||
|
|
72fbe4c904 | ||
|
|
ed5d386a55 | ||
|
|
1d35e90880 | ||
|
|
8826c0c4a1 | ||
|
|
f179ed4e40 | ||
|
|
1515edbbbd | ||
|
|
02d8c243c4 | ||
|
|
fd56e12c9e | ||
|
|
0b5ce44b55 | ||
|
|
fe81461d51 | ||
|
|
2a8500a849 | ||
|
|
1d2f8f2b75 | ||
|
|
fe429a13dc | ||
|
|
5b55263775 | ||
|
|
a5ceea32cf | ||
|
|
d3d770736f | ||
|
|
e09916e232 | ||
|
|
c681df7058 | ||
|
|
91f2925c63 | ||
|
|
416599a85e | ||
|
|
0dde57b401 | ||
|
|
a64303fe1e | ||
|
|
02fade2077 | ||
|
|
518e1a0124 | ||
|
|
bae58cf614 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
The CLI starts faster: each command now loads its implementation only when it runs. `openspec --version` and `--help` load 24 modules instead of 485, and commands such as `config list`, `store list` and `doctor` load only what they use, which matters most where Node loads modules slowly, such as Windows. Output, help text, shell completions, exit codes and telemetry are unchanged.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
A requirement description over 500 characters is now a warning instead of an informational hint, so `openspec validate --strict` fails on it and CI can enforce the limit. The check also covers ADDED requirements in a change, so `openspec validate <change> --strict` catches a new overlong requirement before archive. Normal validation and archive are unchanged: they still pass when this is the only finding. The specs instruction now explains how to split an existing long requirement without losing its scenarios.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
`openspec view` no longer lists or counts archived changes. In projects with many archived changes, the list pushed active work off the screen. The dashboard shows current work again, and `openspec list --archived` still shows archived changes on request.
|
||||
@@ -1,2 +1,5 @@
|
||||
# Default code ownership
|
||||
* @Fission-AI/openspec-maintainers
|
||||
|
||||
# Route docs-lab changes to the docs owner for review
|
||||
/docs-lab/ @TabishB
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Bug report
|
||||
description: Something in OpenSpec does not work the way it should.
|
||||
labels: ['bug', 'needs-triage']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting this.
|
||||
|
||||
You can also run `openspec feedback "your report"` to submit an issue
|
||||
immediately with your version and platform, or get a submission link
|
||||
if GitHub CLI is unavailable or unauthenticated.
|
||||
|
||||
- type: textarea
|
||||
id: what_happened
|
||||
attributes:
|
||||
label: What happened
|
||||
description: The actual behavior. Paste the command you ran and its output if you have it.
|
||||
placeholder: |
|
||||
I ran `openspec archive add-login` and it exited 0 without writing the main spec.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What you expected instead
|
||||
placeholder: The main spec at openspec/specs/auth/spec.md should have been updated.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Minimal steps to reproduce
|
||||
description: The shortest path from a fresh project to the problem. This is the single most useful thing you can give us.
|
||||
placeholder: |
|
||||
1. `openspec init` in an empty directory
|
||||
2. ...
|
||||
3. ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: OpenSpec version
|
||||
description: Output of `openspec --version`, or "unknown" if installation failed or the command cannot run.
|
||||
placeholder: '1.11.0'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: agent
|
||||
attributes:
|
||||
label: Coding agent and model
|
||||
description: Which agent and model were driving OpenSpec, if any. Behavior often differs between them.
|
||||
placeholder: Claude Code, Opus 4.6
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: environment
|
||||
attributes:
|
||||
label: OS and Node version
|
||||
placeholder: macOS 15.6, Node 22.11.0
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,12 @@
|
||||
# Keep the prefilled blank-issue URL from `openspec feedback` working.
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Core design change
|
||||
url: https://github.com/Fission-AI/OpenSpec/discussions/categories/ideas
|
||||
about: Anything that changes how OpenSpec works at its core starts as a discussion, per CONTRIBUTING step 1.
|
||||
- name: Question or help with your setup
|
||||
url: https://github.com/Fission-AI/OpenSpec/discussions/categories/q-a
|
||||
about: Not sure whether it is a bug? Ask here and we will help you narrow it down.
|
||||
- name: Discord
|
||||
url: https://discord.gg/YctCnvvshC
|
||||
about: Chat with the community and the maintainers.
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Feature request
|
||||
description: Something OpenSpec should do that it does not do yet.
|
||||
labels: ['enhancement', 'needs-triage']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
If this would change OpenSpec's core design, open a
|
||||
[discussion](https://github.com/Fission-AI/OpenSpec/discussions) instead — see
|
||||
[CONTRIBUTING.md](https://github.com/Fission-AI/OpenSpec/blob/main/CONTRIBUTING.md).
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: The problem, in one or two sentences
|
||||
description: What you were trying to do, and where OpenSpec got in the way. Describe the problem, not the solution.
|
||||
placeholder: There is no way to tell which change a spec came from after it is archived.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: who
|
||||
attributes:
|
||||
label: Who this affects
|
||||
description: Just you, everyone on a particular agent, everyone using a particular workflow, or everyone. OpenSpec serves many agents and models, so this shapes whether a change fits.
|
||||
placeholder: Anyone archiving more than a handful of changes.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: tried
|
||||
attributes:
|
||||
label: What you tried
|
||||
description: Existing commands, flags, or workarounds you reached for, and why they fell short.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: What you have in mind
|
||||
description: Optional. A sketch is fine — we will agree on the approach before anyone builds it.
|
||||
validations:
|
||||
required: false
|
||||
@@ -0,0 +1,30 @@
|
||||
Closes #
|
||||
|
||||
<!--
|
||||
No issue yet? Every change starts with one (CONTRIBUTING step 1).
|
||||
Run `openspec feedback "your report"` to submit immediately, or open one here:
|
||||
https://github.com/Fission-AI/OpenSpec/issues/new/choose
|
||||
|
||||
Already discussed instead of filed? Replace the line above with a link to the discussion.
|
||||
-->
|
||||
|
||||
## What this changes
|
||||
|
||||
<!-- What was wrong or missing, and what the new behavior is. Plain language. -->
|
||||
|
||||
## How you verified it
|
||||
|
||||
<!--
|
||||
The failing-then-passing test, repro steps, or before/after output.
|
||||
Run the core checks for code changes:
|
||||
pnpm build && pnpm test && pnpm exec tsc --noEmit && pnpm lint
|
||||
-->
|
||||
|
||||
## Notes
|
||||
|
||||
<!-- Optional: scope limits, follow-ups, anything non-blocking. -->
|
||||
|
||||
---
|
||||
|
||||
- [ ] Ran `pnpm changeset` if this affects users, and committed the file
|
||||
- [ ] If a coding agent wrote this, named the agent and model in the Notes section, and verified the result myself
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
@@ -180,10 +180,15 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22
|
||||
uses: DeterminateSystems/nix-installer-action@3138316df39ed29be04236d7ffc686fa525866aa # v23
|
||||
|
||||
- name: Setup Nix cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@908b263ff629f4cc17666315b7fd3ec127c6244d # v14
|
||||
uses: DeterminateSystems/magic-nix-cache-action@84c0677f58dcedf3b91f8223ce36a9ea5b3c84b7 # v15
|
||||
with:
|
||||
# Dependabot runs cannot access the FlakeHub credentials available to
|
||||
# regular CI, so keep those runs on the GitHub Actions cache.
|
||||
use-flakehub: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && 'disabled' || 'no-preference' }}
|
||||
use-gha-cache: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && 'enabled' || 'no-preference' }}
|
||||
|
||||
# Run the update script before `nix build`, not after. The script recomputes
|
||||
# the pnpmDeps hash from pnpm-lock.yaml and rewrites flake.nix in place, so a
|
||||
@@ -210,6 +215,42 @@ jobs:
|
||||
if: always()
|
||||
run: git checkout -- flake.nix || true
|
||||
|
||||
- name: Test downstream overlay composition
|
||||
run: |
|
||||
# Interpolation belongs to Nix, not the shell.
|
||||
# shellcheck disable=SC2016
|
||||
nix eval --impure --expr '
|
||||
let
|
||||
flake = builtins.getFlake (toString ./.);
|
||||
system = builtins.currentSystem;
|
||||
pkgs = import flake.inputs.nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ flake.overlays.default ];
|
||||
};
|
||||
composed = import flake.inputs.nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
flake.overlays.default
|
||||
(_final: prev: {
|
||||
nodejs_22 = prev.nodejs_22.overrideAttrs (_: {
|
||||
pname = "openspec-test-nodejs";
|
||||
});
|
||||
})
|
||||
];
|
||||
};
|
||||
overridden = pkgs.openspec.overrideAttrs (_: { version = "0.0.0-test"; });
|
||||
in
|
||||
assert pkgs.openspec.drvPath == flake.packages.${system}.default.drvPath;
|
||||
assert pkgs.openspec.drvPath == flake.packages.${system}.openspec.drvPath;
|
||||
# stdenv selects the dev output of multi-output native build inputs.
|
||||
assert builtins.any (input: input.drvPath == composed.nodejs_22.drvPath)
|
||||
composed.openspec.nativeBuildInputs;
|
||||
assert composed.openspec.drvPath != pkgs.openspec.drvPath;
|
||||
assert overridden.version == "0.0.0-test";
|
||||
assert overridden.pnpmDeps.version == "0.0.0-test";
|
||||
true
|
||||
'
|
||||
|
||||
- name: Build with Nix
|
||||
run: nix build
|
||||
|
||||
@@ -281,7 +322,7 @@ jobs:
|
||||
|
||||
- name: Setup pnpm
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# Opens/updates the Version Packages PR; publishes when the Version PR merges
|
||||
- name: Create/Update Version PR
|
||||
id: changesets
|
||||
uses: changesets/action@8488615a623b1b9c987934bb89eae8af6a946ac1 # v2.1.1
|
||||
uses: changesets/action@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2
|
||||
with:
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
pr-title: 'chore(release): version packages'
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
# No dependency cache: `pnpm audit` reads the lockfile, nothing is installed,
|
||||
# so a cache-save step would fail on the missing store path.
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
|
||||
+143
@@ -1,5 +1,148 @@
|
||||
# @fission-ai/openspec
|
||||
|
||||
## 1.14.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#883](https://github.com/Fission-AI/OpenSpec/pull/883) [`c879d13`](https://github.com/Fission-AI/OpenSpec/commit/c879d13d5f5d045c532a08523316d2d74f2db99a) Thanks [@Code-Studio-Team](https://github.com/Code-Studio-Team)! - Add Code Studio as an `init` and `update` target, with project skills and `.prompt.md` commands under `.codestudio/`.
|
||||
|
||||
- [#1672](https://github.com/Fission-AI/OpenSpec/pull/1672) [`297092c`](https://github.com/Fission-AI/OpenSpec/commit/297092cb25d9831a408d2ce9bfd55daec1431b74) Thanks [@DarkskyX15](https://github.com/DarkskyX15)! - - **DeepSeek Harness** — `openspec init --tools dsh` (command-line id `dsh`) installs the OpenSpec workflow skills into `.dsh/skills/` for DeepSeek Harness. It is skills-only (no command adapter or command files): dsh discovers the generated `SKILL.md` files as its highest-priority project root and surfaces them through its skill catalog, `skill` tool, and `/openspec-*` user invocations.
|
||||
|
||||
- [#1961](https://github.com/Fission-AI/OpenSpec/pull/1961) [`3c3e6e3`](https://github.com/Fission-AI/OpenSpec/commit/3c3e6e3d423625ffe554ff050c09bb530f17dc5e) Thanks [@fresh-fx59](https://github.com/fresh-fx59)! - Add GigaCode as a supported `--tools` target, with skills in `.gigacode/skills/openspec-*/SKILL.md` and Markdown commands in `.gigacode/commands/opsx-<id>.md`.
|
||||
|
||||
- [#1211](https://github.com/Fission-AI/OpenSpec/pull/1211) [`3de7c72`](https://github.com/Fission-AI/OpenSpec/commit/3de7c72c267c40ff89809d2ae08b7bf6ac6faf8c) Thanks [@hu-qi](https://github.com/hu-qi)! - Add AtomCode support through `openspec init --tools atomcode`, with project skills in `.atomcode/skills/` and `/opsx-<id>` commands in `.atomcode/commands/`. Generated commands declare `args: optional` and receive `$ARGUMENTS` when the workflow reads invocation input, and `args: none` when it does not, so AtomCode runs them straight from the slash menu. Follows the selected workflow profile and delivery mode.
|
||||
|
||||
- [#1082](https://github.com/Fission-AI/OpenSpec/pull/1082) [`a7f08b8`](https://github.com/Fission-AI/OpenSpec/commit/a7f08b8a462db5eeddae4e0b03f427987e3806a2) Thanks [@Storm-Chaser](https://github.com/Storm-Chaser)! - ### New Features
|
||||
|
||||
- **GSD support**: Install OpenSpec workflows as project skills with `openspec init --tools gsd`.
|
||||
|
||||
- [#420](https://github.com/Fission-AI/OpenSpec/pull/420) [`070de01`](https://github.com/Fission-AI/OpenSpec/commit/070de01dfac4ea343747fbd37b9bb9e77acdf7d0) Thanks [@jeanduplessis](https://github.com/jeanduplessis)! - ### New Features
|
||||
|
||||
- **Amp support**: select `amp` during init to install OpenSpec workflows as project skills under `.agents/skills/`.
|
||||
|
||||
- [#2001](https://github.com/Fission-AI/OpenSpec/pull/2001) [`56528ea`](https://github.com/Fission-AI/OpenSpec/commit/56528ea454a926159d05eb7c9ec1b687d7544b56) Thanks [@clay-good](https://github.com/clay-good)! - ### New Features
|
||||
|
||||
- **Version reports** — Run `openspec version` to inspect the installed version and install type, or add `--check` and `--json` for structured update information that tools can consume.
|
||||
|
||||
- [#1352](https://github.com/Fission-AI/OpenSpec/pull/1352) [`d1642cb`](https://github.com/Fission-AI/OpenSpec/commit/d1642cb58cb4ce2cda0a140cf2322aded53f4e46) Thanks [@redknox](https://github.com/redknox)! - Add EasyCode support to init and update, with project-local skills and TOML commands invoked as `/opsx:<id>`.
|
||||
|
||||
- [#399](https://github.com/Fission-AI/OpenSpec/pull/399) [`ded99e2`](https://github.com/Fission-AI/OpenSpec/commit/ded99e27de71c32647ae7fc2f51112219420b5d5) Thanks [@ZEDce](https://github.com/ZEDce)! - Add `openspec list --archived` and `--all` to browse archived changes, including JSON output and sorting. Show archived changes separately in the `openspec view` dashboard.
|
||||
|
||||
- [#848](https://github.com/Fission-AI/OpenSpec/pull/848) [`5a360c2`](https://github.com/Fission-AI/OpenSpec/commit/5a360c2088ad3094a092c34993eab97b43c25124) Thanks [@Columpio](https://github.com/Columpio)! - ### New Features
|
||||
|
||||
- **Veai support**: select `veai` during init to install OpenSpec workflows as project skills under `.veai/skills/`.
|
||||
|
||||
- [#1439](https://github.com/Fission-AI/OpenSpec/pull/1439) [`f197804`](https://github.com/Fission-AI/OpenSpec/commit/f197804a38057eee2272952b74d89884a9d3b7a4) Thanks [@jmuchovej](https://github.com/jmuchovej)! - Expose OpenSpec as a reusable Nix overlay through `overlays.default`.
|
||||
|
||||
- [#1349](https://github.com/Fission-AI/OpenSpec/pull/1349) [`e232080`](https://github.com/Fission-AI/OpenSpec/commit/e232080d0943bd535388bdc2304b3301f1496226) Thanks [@0x6d6e647a](https://github.com/0x6d6e647a)! - Add Grok Build as a skills-only tool. Run `openspec init --tools grok` to install skills in `.grok/skills`, then invoke them with `/openspec-propose` and other skill names. Existing Grok installations are refreshed by `openspec update`.
|
||||
|
||||
- [#1738](https://github.com/Fission-AI/OpenSpec/pull/1738) [`781c7f9`](https://github.com/Fission-AI/OpenSpec/commit/781c7f9447b4eeb6fdc69fa745ff46f6168f3edf) Thanks [@clay-good](https://github.com/clay-good)! - Add Warp support through project-local skills. Select `warp` during init to install OpenSpec workflows in `.warp/skills`, invoke them with `/openspec-*`, and refresh them with `openspec update`. Skills remain available in every delivery mode.
|
||||
|
||||
- [#807](https://github.com/Fission-AI/OpenSpec/pull/807) [`c21d897`](https://github.com/Fission-AI/OpenSpec/commit/c21d897261b5daf0c61c49ccd0862288d4664db4) Thanks [@Million-mo](https://github.com/Million-mo)! - ### New Features
|
||||
|
||||
- **Dashboard workflow status**: `openspec view` now shows each active change's schema and which artifacts are done, ready, blocked, or skipped. Task progress remains visible if a workflow cannot be loaded. Thanks to @Million-mo for the original contribution in [#807](https://github.com/Fission-AI/OpenSpec/issues/807).
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1977](https://github.com/Fission-AI/OpenSpec/pull/1977) [`7728194`](https://github.com/Fission-AI/OpenSpec/commit/772819417a2aa8a90cd50743139f402261628d21) Thanks [@clay-good](https://github.com/clay-good)! - The `openspec-archive-change` skill no longer tells the agent to run the `openspec-sync-specs` skill when that skill is not installed. It merges the delta specs into the main specs itself instead, as the `/opsx:archive` command already did ([#1975](https://github.com/Fission-AI/OpenSpec/issues/1975)).
|
||||
|
||||
- [#1722](https://github.com/Fission-AI/OpenSpec/pull/1722) [`817cdb6`](https://github.com/Fission-AI/OpenSpec/commit/817cdb64be744d4ee65d1a9922b23edc0c4699b8) Thanks [@caseyg](https://github.com/caseyg)! - ### Bug Fixes
|
||||
|
||||
- IBM Bob now appears by its full product name in the tool picker and success messages. Existing `bob` selections, configuration, skills, and slash-command paths continue to work unchanged.
|
||||
|
||||
- [#1999](https://github.com/Fission-AI/OpenSpec/pull/1999) [`bda8556`](https://github.com/Fission-AI/OpenSpec/commit/bda85565ef974d07c1c202c0ac4b2613241dd184) Thanks [@clay-good](https://github.com/clay-good)! - Guide users through an AI-assisted migration from legacy `project.md` to `config.yaml`.
|
||||
|
||||
- [#1997](https://github.com/Fission-AI/OpenSpec/pull/1997) [`e70dcc7`](https://github.com/Fission-AI/OpenSpec/commit/e70dcc7c82a3b100145795d32ea9930dca7b4073) Thanks [@clay-good](https://github.com/clay-good)! - Guide proposal authors toward durable, behavior-based capability names.
|
||||
|
||||
- [#2018](https://github.com/Fission-AI/OpenSpec/pull/2018) [`81c2f9f`](https://github.com/Fission-AI/OpenSpec/commit/81c2f9fce30d103bd22377042af1d428453b0bbc) Thanks [@clay-good](https://github.com/clay-good)! - ### Bug Fixes
|
||||
|
||||
- **Apply edits the right task** — `openspec instructions apply --json` now gives each task its `sourcePath` and `line`. The apply workflow checks the checkbox at that location before marking the task done and rechecks progress afterward, so agents update the exact task, even when tasks span several files.
|
||||
- **Archive stops on a failed spec sync** — When the spec sync inside `/opsx:archive` reports a blocking condition, such as a capability retirement it could not complete, the archive now stops and leaves the change in place instead of archiving it with the main specs unchanged. The same applies to bulk archive.
|
||||
- **Aligned `openspec view` progress bars** — Active change names up to 48 characters now line up their progress bars instead of pushing each bar out of line.
|
||||
|
||||
- [#1969](https://github.com/Fission-AI/OpenSpec/pull/1969) [`9557b43`](https://github.com/Fission-AI/OpenSpec/commit/9557b43aaff05af8bd9862c4755f7ac7b7f43cf1) Thanks [@flcrom](https://github.com/flcrom)! - ### Bug Fixes
|
||||
|
||||
- Let store-only repositories run `openspec init` at the repo root to install integrations without changing the external-store config or creating local planning directories.
|
||||
|
||||
- [#2004](https://github.com/Fission-AI/OpenSpec/pull/2004) [`d4e1c77`](https://github.com/Fission-AI/OpenSpec/commit/d4e1c77ebae0bd96a7c649fa35edef997989450a) Thanks [@clay-good](https://github.com/clay-good)! - Warn that files listed for legacy cleanup are deleted entirely and ask users to back up custom content first. The `openspec/AGENTS.md` check detects the file by existence alone.
|
||||
|
||||
- [#1995](https://github.com/Fission-AI/OpenSpec/pull/1995) [`baad449`](https://github.com/Fission-AI/OpenSpec/commit/baad4494b48f1497ec2f73a457210c5567176942) Thanks [@clay-good](https://github.com/clay-good)! - Guide agents to keep project documentation and codebase facts out of `config.yaml` context.
|
||||
|
||||
- [#1978](https://github.com/Fission-AI/OpenSpec/pull/1978) [`1872982`](https://github.com/Fission-AI/OpenSpec/commit/187298289dc5a7a63df87425151981586cbe5d7e) Thanks [@clay-good](https://github.com/clay-good)! - The specs instruction now tells agents the 500-character requirement length that `openspec validate` flags as an informational hint, and how to stay under it when writing new requirements without splitting existing ones. The validator's too-long message now explains how to split a requirement too.
|
||||
|
||||
- [#1972](https://github.com/Fission-AI/OpenSpec/pull/1972) [`d28fb49`](https://github.com/Fission-AI/OpenSpec/commit/d28fb49c1ca901fe19fa443a56ede37ff8b8c61a) Thanks [@ryandemelo](https://github.com/ryandemelo)! - `show --json` now includes each requirement's and scenario's `name`, matching the header names archive uses, so JSON readers can cite a requirement without parsing the markdown again ([#1971](https://github.com/Fission-AI/OpenSpec/issues/1971)).
|
||||
|
||||
- [#2014](https://github.com/Fission-AI/OpenSpec/pull/2014) [`cf2859a`](https://github.com/Fission-AI/OpenSpec/commit/cf2859a52089dd6dd37f9b3388db90c2ca3f06e7) Thanks [@clay-good](https://github.com/clay-good)! - Fix `status --json` for store-backed changes: `actionContext.allowedEditRoots` now lists the project on the current path that declares the store alongside the store, so apply no longer stops on a store-only edit scope. When no project on the current path declares the store, the constraint tells the agent to ask which repository to edit instead of naming the store.
|
||||
|
||||
- [#1984](https://github.com/Fission-AI/OpenSpec/pull/1984) [`42671df`](https://github.com/Fission-AI/OpenSpec/commit/42671df890fab730058fee108a2090e7c1e491b9) Thanks [@Yi-111-a](https://github.com/Yi-111-a)! - Name the offending index when a `rules:` list is not an array of strings
|
||||
|
||||
A rule item containing an unquoted `": "` is valid-looking YAML but parses as a
|
||||
mapping, so the artifact's whole rule set is dropped with only a stderr warning
|
||||
naming the artifact. The warning now also names the index and the shape YAML
|
||||
produced there, plus the quoting fix, so the bad item can be found without
|
||||
bisecting the list by hand.
|
||||
|
||||
- [#1925](https://github.com/Fission-AI/OpenSpec/pull/1925) [`88692b3`](https://github.com/Fission-AI/OpenSpec/commit/88692b3bb42262d30172819936847ed10f42a98f) Thanks [@kevin9327](https://github.com/kevin9327)! - ### Bug Fixes
|
||||
|
||||
- **Change metadata** — Warn when `.openspec.yaml` contains unrecognized keys such as `skip_design`. Those keys were stripped with no signal, so `status` still demanded the design artifact and `validate --strict` exited 0. `status`, `validate`, and `archive` now name the ignored keys; `validate --strict` fails.
|
||||
|
||||
- [#2016](https://github.com/Fission-AI/OpenSpec/pull/2016) [`cd4f9e4`](https://github.com/Fission-AI/OpenSpec/commit/cd4f9e4a5f99e7b48f2c452ef0fa3769db4dde4a) Thanks [@huiq777](https://github.com/huiq777)! - Make `openspec completion uninstall zsh` hand `.zshrc` back exactly as `completion install zsh` found it. Uninstall stripped every blank line at the top of the file, so a `.zshrc` that started with blank lines lost them after an install/uninstall round trip, even when the OpenSpec block had been moved further down. Uninstall now drops only the separator line install added, and only when the block sits at the top of the file, matching the bash installer.
|
||||
|
||||
## 1.13.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1940](https://github.com/Fission-AI/OpenSpec/pull/1940) [`0b5ce44`](https://github.com/Fission-AI/OpenSpec/commit/0b5ce44b55e0d793a312290ba5a41170a78e47c6) Thanks [@clay-good](https://github.com/clay-good)! - Keep fast-forward clarification guidance and onboarding task approval consistent across generated skills and commands. Fast-forward now asks only when context is critically unclear, while onboarding asks users to approve the task breakdown before saving it and separately asks whether to begin implementation.
|
||||
|
||||
- [#1926](https://github.com/Fission-AI/OpenSpec/pull/1926) [`f2812f6`](https://github.com/Fission-AI/OpenSpec/commit/f2812f6d185f47cb577055f2fe243f12000d6cd2) Thanks [@kevin9327](https://github.com/kevin9327)! - ### Bug Fixes
|
||||
|
||||
- **Archive** — When Windows `EPERM` blocks renaming a change directory that still has children, copy from the original source instead of requiring a staging rename that fails the same way. That lets archive finish instead of rolling back the spec write and leaving an empty capability directory git cannot see. A staging failure that is not `EPERM`/`EXDEV` still leaves the source untouched.
|
||||
|
||||
The source of that unstaged copy is still the live change directory, which the archive claim does not cover, so cleanup removes only the entries it copied and verified rather than whatever is present when it runs. A file written in that window is left alone and the complete destination is retained for recovery, instead of being deleted without ever reaching the archive.
|
||||
|
||||
An edit to a file that was already verified is covered too. Cleanup claims each entry with an atomic rename before reading it, then compares what it claimed against the copy. A rewrite that lands first is caught by that comparison and the file is put back; one that lands after creates a new file at the original path, which is never deleted. Either way the newer bytes stay on disk and archive reports the move as incomplete rather than succeeding with the older copy.
|
||||
|
||||
Rollback of a newly created spec now also prunes the capability directory it created — and only that one. An empty capability directory that was already there is left in place with its own permissions.
|
||||
|
||||
- [#1795](https://github.com/Fission-AI/OpenSpec/pull/1795) [`fb1b876`](https://github.com/Fission-AI/OpenSpec/commit/fb1b87613b7cdbe8d74e8147833904f46f0468c6) Thanks [@runsonmypc](https://github.com/runsonmypc)! - Archive workflows now use schema-aware task progress from `openspec list --json`, so custom task files and globs still trigger incomplete-task warnings.
|
||||
|
||||
- [#1885](https://github.com/Fission-AI/OpenSpec/pull/1885) [`fd56e12`](https://github.com/Fission-AI/OpenSpec/commit/fd56e12c9e7fdbbfdc2dcd0a5ef3fab04840909d) Thanks [@philo-x](https://github.com/philo-x)! - Fix artifact output resolution to recognize brace expansion and extglob patterns while preserving literal output filenames and confining brace-expanded paths to the change directory.
|
||||
|
||||
- [#1964](https://github.com/Fission-AI/OpenSpec/pull/1964) [`7ac58dc`](https://github.com/Fission-AI/OpenSpec/commit/7ac58dc7905a4eeaaad7eff2b2b64cf971fd6ec7) Thanks [@clay-good](https://github.com/clay-good)! - Continue commands now open with an instruction to follow the active OpenSpec workflow directly, so local models no longer try to call a tool named after it ([#1944](https://github.com/Fission-AI/OpenSpec/issues/1944)).
|
||||
|
||||
- [#1964](https://github.com/Fission-AI/OpenSpec/pull/1964) [`7ac58dc`](https://github.com/Fission-AI/OpenSpec/commit/7ac58dc7905a4eeaaad7eff2b2b64cf971fd6ec7) Thanks [@clay-good](https://github.com/clay-good)! - Generate Kilo Code commands in `.kilo/command/`, the directory Kilo Code reads, instead of `.kilocode/workflows/` ([#1938](https://github.com/Fission-AI/OpenSpec/issues/1938)). `openspec init` and legacy cleanup remove the workflow files OpenSpec generated there, matched by their known file names (including copies you edited), and leave files with other names in place.
|
||||
|
||||
- [#1958](https://github.com/Fission-AI/OpenSpec/pull/1958) [`1d35e90`](https://github.com/Fission-AI/OpenSpec/commit/1d35e908804dbb3c4a1851516759c5de190aa4d5) Thanks [@clay-good](https://github.com/clay-good)! - Preserve a file's existing line endings when rewriting it, so Windows users no longer get whole-file diffs. Applying a delta to a CRLF spec (the default on a Windows checkout with `core.autocrlf=true`) rewrote the file to LF, turning a one-requirement change into a diff that touched every line. `openspec archive` now writes the spec back with the convention it already used; a spec that does not exist yet is still written with LF.
|
||||
|
||||
The same fix covers marker-managed files: installing or updating shell completions in a CRLF `.bashrc` or `.zshrc` no longer leaves the file with mixed endings, which `bash` reports as `$'\r': command not found`.
|
||||
|
||||
Removing a managed block is fixed the same way: the blank-line collapse in `removeMarkerBlock` rebuilt its separator as a bare LF, so cleaning up legacy artifacts left a lone LF inside an otherwise-CRLF `CLAUDE.md` or rc file. Both write paths now read the file the same way, by dominant ending, so one stray CRLF in an otherwise-LF file no longer pulls the whole rewrite to CRLF.
|
||||
|
||||
`scripts/pack-version-check.mjs` now spawns `npm` through `cross-spawn`, so the release guard can run on Windows, where `npm` is `npm.cmd` and cannot be resolved by `execFile`.
|
||||
|
||||
- [#1912](https://github.com/Fission-AI/OpenSpec/pull/1912) [`8826c0c`](https://github.com/Fission-AI/OpenSpec/commit/8826c0c4a17d3511947b7c5e0934257f153f0ed2) Thanks [@Tyagiquamar](https://github.com/Tyagiquamar)! - Fix `validate --strict` reporting `PURPOSE_IS_PLACEHOLDER` for a Purpose that opens with the ordinary word "Todo" followed by prose, as in Spanish ("Todo el…") and Portuguese ("Todo o…") specs ([#1897](https://github.com/Fission-AI/OpenSpec/issues/1897)).
|
||||
|
||||
- Case now separates the marker from the word. `TBD`/`TODO` in capitals is still a placeholder marker whatever follows it, so `TODO write this later` is still reported.
|
||||
- In any other case it counts as a marker only when followed by the end of the Purpose, a line break, or marker punctuation (`todo -`, `tbd.`), so an authored Spanish or Portuguese sentence is not reported.
|
||||
|
||||
- [#1744](https://github.com/Fission-AI/OpenSpec/pull/1744) [`5b55263`](https://github.com/Fission-AI/OpenSpec/commit/5b5526377506c2f0179674a869c1ac64ca9ab72d) Thanks [@javigomez](https://github.com/javigomez)! - Clarify the Codex setup hint for CLI, IDE, and desktop app users.
|
||||
|
||||
- [#1809](https://github.com/Fission-AI/OpenSpec/pull/1809) [`a5ceea3`](https://github.com/Fission-AI/OpenSpec/commit/a5ceea32cf110b6d8bbfea0bf1c65fe55abb133b) Thanks [@ryandemelo](https://github.com/ryandemelo)! - Say what a `MODIFIED` block adds when the scenario-loss guard fires ([#1809](https://github.com/Fission-AI/OpenSpec/pull/1809)). `openspec validate` and `openspec archive` already named the scenarios a block omits. They now also print how many scenarios each side has and which ones the block introduces, capped at three names, so a rename and a truncation read differently without opening either file. The guard catches exactly what it did before, and no exit code changes.
|
||||
|
||||
- [#1731](https://github.com/Fission-AI/OpenSpec/pull/1731) [`d6bdef6`](https://github.com/Fission-AI/OpenSpec/commit/d6bdef6577a077614382ef47b64100852182d6a6) Thanks [@runsonmypc](https://github.com/runsonmypc)! - Stop workflows from displaying schema names that `openspec list --json` does not return. Update and continue no longer fabricate a `spec-driven` picker label, while bulk archive and explore describe only the change fields the list command actually provides.
|
||||
|
||||
- [#1955](https://github.com/Fission-AI/OpenSpec/pull/1955) [`ed5d386`](https://github.com/Fission-AI/OpenSpec/commit/ed5d386a559c0215af1182d479d7f99b309fdcd2) Thanks [@clay-good](https://github.com/clay-good)! - Task guidance now requires each task group to land its own tests and documentation updates instead of deferring them to a trailing group. The onboarding walkthrough teaches the same rule, and the published schema reference no longer quotes stale instruction text.
|
||||
|
||||
- [#1939](https://github.com/Fission-AI/OpenSpec/pull/1939) [`a64303f`](https://github.com/Fission-AI/OpenSpec/commit/a64303fe1e24f08dbf44f78032fadbeac3a6f7fa) Thanks [@clay-good](https://github.com/clay-good)! - Return a nonzero exit status when `openspec update --force` cannot replace a legacy-only Codex installation.
|
||||
|
||||
- [#1733](https://github.com/Fission-AI/OpenSpec/pull/1733) [`72fbe4c`](https://github.com/Fission-AI/OpenSpec/commit/72fbe4c904707396151921a20b506d081a9dc024) Thanks [@runsonmypc](https://github.com/runsonmypc)! - Let `/opsx:update` fill a missing file under an already-satisfied glob artifact. A glob artifact is complete once one file matches it, and `/opsx:continue` only picks up `ready` artifacts, so the previous "point the user to `/opsx:continue`" handoff was unreachable and the missing file could never be created through the documented flow.
|
||||
|
||||
- [#1962](https://github.com/Fission-AI/OpenSpec/pull/1962) [`3364146`](https://github.com/Fission-AI/OpenSpec/commit/336414665f3f987ae424177ab1b6891a4304baeb) Thanks [@ryandemelo](https://github.com/ryandemelo)! - Stop `/opsx:verify` from reporting a correctly removed requirement as missing. Verify now reads which delta section each requirement sits under: ADDED and MODIFIED requirements are checked for an implementation as before, a REMOVED requirement passes once its behavior is gone and is flagged only while it is still present, and the old name of a RENAMED requirement is no longer reported as missing.
|
||||
|
||||
- [#1732](https://github.com/Fission-AI/OpenSpec/pull/1732) [`072de6b`](https://github.com/Fission-AI/OpenSpec/commit/072de6bc39b1c47b9aacf4d484be16345ca4f38e) Thanks [@runsonmypc](https://github.com/runsonmypc)! - Stop `/opsx:verify` from reporting skipped checks as passing. Task completion now uses the schema-aware `tasks` and `progress` fields returned by apply instructions, while absent spec or design inputs are mapped to every check they prevent. Apply instructions aggregate every file matched by the configured task path or glob, regardless of the tracked artifact ID. Verification stays advisory and does not require optional or intentionally omitted artifacts. The scorecard identifies each skipped check, and the final assessment does not claim archive readiness when any check did not run.
|
||||
|
||||
- [#1769](https://github.com/Fission-AI/OpenSpec/pull/1769) [`d3d7707`](https://github.com/Fission-AI/OpenSpec/commit/d3d770736fc01bb246b4f12a7cef7e3572ec1fb6) Thanks [@kikeprzn](https://github.com/kikeprzn)! - Fix `openspec archive` leaving `.openspec-archive.lock` behind on Windows. Node can report `dev: 0n` from a path stat while the open file handle reports the real volume id, so the claim-ownership check never matched and the stale lock blocked every later archive. The check now treats an absent device id as unavailable while still requiring the inode and the claim's contents to match before unlinking.
|
||||
|
||||
## 1.13.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -37,6 +37,15 @@ Those four commands are what CI runs, so a green local run means a green CI run.
|
||||
|
||||
Run `pnpm changeset` if your change affects users, and commit the file it generates.
|
||||
|
||||
### Keep the CLI's startup fast
|
||||
|
||||
Editors, agents and OpenSpec Desktop run the CLI many times, and each call pays for every module it loads before the command runs. Before this rule, `openspec --version` loaded 485 modules: about 0.5 s per call on a Windows machine. So a command loads only the command definitions and its own code:
|
||||
|
||||
- **Definitions** (name, options, help text) go in `src/cli/index.ts` or `src/cli/commands/<name>.ts`. Import nothing heavy there: no zod, yaml, fast-glob, ora, and no other command's code.
|
||||
- **The command's code** goes in `src/commands/<name>.ts` or `src/core/`, loaded inside the action with `await import()`.
|
||||
|
||||
`test/cli-e2e/startup-modules.test.ts` checks which modules each command loads, and fails if a definition starts pulling in an implementation. When you add a command, add it to that test's list.
|
||||
|
||||
## 4. Open the PR
|
||||
|
||||
- Branch off `main` in your fork.
|
||||
|
||||
@@ -122,7 +122,7 @@ Solo, OpenSpec keeps you and your AI honest on a single repo. On a team, the har
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Requires Node.js 20.19.0 or higher.**
|
||||
**Requires Node.js 20.19.0 or higher.** Homebrew installs it as a dependency.
|
||||
|
||||
Install OpenSpec globally:
|
||||
|
||||
@@ -130,6 +130,12 @@ Install OpenSpec globally:
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
Or install the official [Homebrew formula](https://formulae.brew.sh/formula/openspec) on macOS or Linux:
|
||||
|
||||
```bash
|
||||
brew install openspec
|
||||
```
|
||||
|
||||
Then navigate to your project directory and initialize:
|
||||
|
||||
```bash
|
||||
@@ -137,7 +143,7 @@ cd your-project
|
||||
openspec init
|
||||
```
|
||||
|
||||
> **Want your AI to do it?** Paste the [setup prompt](docs/installation.md#install-with-your-ai-assistant) into your coding assistant — it installs the CLI, runs `openspec init`, and verifies the result.
|
||||
> **Want your AI to do it?** Paste the [setup prompt](docs-lab/start/installation.md#install-with-your-ai-assistant) into your coding assistant — it installs the CLI, runs `openspec init`, and verifies the result.
|
||||
|
||||
Now talk to your AI:
|
||||
|
||||
@@ -151,7 +157,7 @@ Both are in the default profile. If you want the expanded workflow (`/opsx:new`,
|
||||
> [!NOTE]
|
||||
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 30+ tools and growing.
|
||||
>
|
||||
> Also works with pnpm, yarn, bun, and nix. [See installation options](docs/installation.md).
|
||||
> Also works with Homebrew, pnpm, yarn, bun, and Nix. [See installation options](docs-lab/start/installation.md).
|
||||
|
||||
## Docs
|
||||
|
||||
@@ -208,6 +214,12 @@ AI coding assistants are powerful but unpredictable when requirements live only
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
If you installed OpenSpec with Homebrew:
|
||||
|
||||
```bash
|
||||
brew upgrade openspec
|
||||
```
|
||||
|
||||
**Refresh agent instructions**
|
||||
|
||||
Run this inside each project to regenerate AI guidance and ensure the latest slash commands are active:
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ the page or rewriting the goal in both places, never letting them drift.
|
||||
| [Overview](start/overview.md) | _TODO: emptied 2026-08-21 for a from-scratch rewrite and pulled from the site (`/docs` redirects to Installation meanwhile); the old goal line was dropped as too weak a pitch. Brief in Notes.md._ |
|
||||
| [Installation](start/installation.md) | Install the `openspec` CLI on your machine, update it, and uninstall it. |
|
||||
| [Set up your project](start/setup.md) | Add OpenSpec to a project: run init, see what it wrote, and adjust it. |
|
||||
| [Quickstart](start/quickstart.md) | Your first change on your existing repo, from idea to archived. |
|
||||
| [Quickstart](start/quickstart.md) | Your first change in a new or existing project, from idea to archived. |
|
||||
|
||||
### Guides: understand the system, use it well, bring it to your codebase and team
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ For example, with a `context` field and the rule from the top of this page, here
|
||||
|
||||
<!-- From your config.yaml: context -->
|
||||
<project_context>
|
||||
Tech stack: TypeScript, Node.js
|
||||
Domain: e-commerce platform
|
||||
Designs and tasks must cover Windows, macOS, and Linux
|
||||
Write all artifacts in Spanish
|
||||
</project_context>
|
||||
|
||||
<!-- From your config.yaml: rules for tasks -->
|
||||
@@ -58,8 +58,6 @@ For example, with a `context` field and the rule from the top of this page, here
|
||||
|
||||
Your config arrives first, then OpenSpec's built-in instruction and template. Rules add to the built-ins and never replace them. Edits to config.yaml reach the agent on the next run.
|
||||
|
||||
[Workflow runs](../reference/architecture/workflow-runs.md) covers the full run, from invocation to written artifacts.
|
||||
|
||||
## The fields
|
||||
|
||||
Three fields shape what the agent receives. Each field's exact contract (types, limits, validation) is in [Project configuration (config.yaml)](../reference/configuration/config-yaml.md).
|
||||
@@ -76,18 +74,17 @@ The last column is exact, so a field reaches only the steps listed there. In par
|
||||
|
||||
### context
|
||||
|
||||
`context` is what the agent should know up front when planning a change, whether it's creating an artifact, applying tasks, or archiving:
|
||||
`context` is background the agent receives when it creates an artifact, applies tasks, or archives a change.
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
We ship cross-platform; designs and tasks must cover Windows, macOS, and Linux
|
||||
Tech stack: TypeScript, Node.js, Commander.js
|
||||
We use conventional commits
|
||||
We ship cross-platform. Designs and tasks must cover Windows, macOS, and Linux
|
||||
Write all artifacts in Spanish
|
||||
```
|
||||
|
||||
This is planning context, not project documentation. Add a fact when it should shape every plan, like the cross-platform line above. Leave out anything the agent can learn by reading the code.
|
||||
Use `context` for facts and constraints that should shape workflow output. Keep durable project documentation in the project's documentation files, and leave out anything the agent can learn by reading the code.
|
||||
|
||||
**Another language**: because context reaches every artifact, it's also how you change the output language. One line, like `Write all artifacts in Spanish.`, switches every proposal, spec, and tasks file the workflows write.
|
||||
**Another language**: because context reaches every artifact, you can add `Write all artifacts in Spanish.` to instruct the agent to write proposal, spec, design, and tasks artifacts in Spanish.
|
||||
|
||||
### rules
|
||||
|
||||
|
||||
@@ -162,3 +162,9 @@ Sharing a schema means copying its folder.
|
||||
- **From the community**: the [community catalog](https://github.com/Fission-AI/OpenSpec/blob/main/docs/customization.md#community-schemas) lists shared schemas. Copy one into `openspec/schemas/<name>` and it works like your own.
|
||||
|
||||
We're working on a schema registry, public and private, so schemas can be installed by name instead of copied by hand.
|
||||
|
||||
### Use OpenSpec with Superpowers
|
||||
|
||||
The community-maintained [`superpowers-bridge`](https://github.com/JiangWay/openspec-schemas/tree/main/superpowers-bridge) schema connects OpenSpec artifacts to [Superpowers](https://github.com/obra/superpowers) execution skills. Follow the bridge's installation and compatibility notes before copying it into your project.
|
||||
|
||||
The bridge is released outside OpenSpec. OpenSpec does not test or version its Superpowers integration.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> The two-minute pass that catches wrong turns before they're code.
|
||||
|
||||
<!-- Skeleton: headings only. -->
|
||||
<!-- Partial draft: the plan-review sections are still headings only. -->
|
||||
|
||||
## The two-minute pass
|
||||
|
||||
@@ -13,3 +13,24 @@
|
||||
## Pushing back
|
||||
|
||||
## Advanced: verify after apply
|
||||
|
||||
Before archiving, check that the code does what the scenarios describe. The optional
|
||||
[verify skill](../reference/skills.md#openspec-verify-change) can help find gaps.
|
||||
|
||||
### Keep a record when checks are hard to track
|
||||
|
||||
Use the change's `tasks.md` or an existing test report. For each check, record:
|
||||
|
||||
- **Scenario**: the requirement and scenario it checks, with a link to that version of the spec.
|
||||
- **Check**: the test or manual check and what should happen.
|
||||
- **Result**: pass, fail, not run, or unknown. Link to the original run or dated observation.
|
||||
- **Tested version**: the code revision or build tested, and where it ran.
|
||||
|
||||
### Review the results
|
||||
|
||||
- **Open the source.** Confirm the result in the linked run or report. A checked task or an agent's summary alone does not prove the test passed.
|
||||
- **Look for gaps.** Check that every scenario has a result. A passing test on one device or environment does not cover another. Keep missing and failed checks visible.
|
||||
- **Check for changes.** Rerun checks affected by changes to the requirements, code, or environment. Unrelated documentation edits may leave earlier results valid.
|
||||
|
||||
**Archiving does not enforce these checks.** If passing results are required for
|
||||
release, enforce that in your CI or release process.
|
||||
|
||||
@@ -15,4 +15,12 @@ once the prose lands. -->
|
||||
|
||||
## Migrating a project
|
||||
|
||||
### Back up custom content before cleanup
|
||||
|
||||
Files listed under **Files to remove** are deleted entirely. Back up any custom content before accepting cleanup.
|
||||
|
||||
- **`openspec/AGENTS.md`**: detected by existence alone; cleanup does not inspect its contents.
|
||||
- **Root-level `AGENTS.md`, `CLAUDE.md`, and other config files**: cleanup removes OpenSpec marker blocks and preserves content outside those blocks.
|
||||
- **Legacy command directories**: cleanup preserves files it does not recognize as generated commands.
|
||||
|
||||
## Behavior differences
|
||||
|
||||
@@ -223,6 +223,23 @@ No active changes. Create one with: openspec new change <name> --store team-plan
|
||||
- **Commit it**: teammates who clone your project get the line too. They still need the store registered on their machine ([step 3 of Set up a store](#set-up-a-store)), or OpenSpec errors and tells them to register it.
|
||||
- **Next to real folders**: if your project also has `specs/` or `changes/` folders, OpenSpec uses those and ignores the line, with a warning.
|
||||
|
||||
### Install integrations in a store-only repo
|
||||
|
||||
Run init from the code repo's root to install AI tool integration files without moving planning back into that repo:
|
||||
|
||||
```bash
|
||||
# inside web-app, at the repository root
|
||||
openspec init --tools claude
|
||||
```
|
||||
|
||||
- **Integration files**: written in the code repo.
|
||||
- **`openspec/config.yaml`**: preserved byte-for-byte, including the `store:` line.
|
||||
- **`openspec/specs/` and `openspec/changes/`**: not created in the code repo.
|
||||
|
||||
OpenSpec refuses this command from a subdirectory of the code repo. Run it from the repository root.
|
||||
|
||||
OpenSpec also refuses `--language` here because the language belongs in the external store's config. Run init in the store root or edit that config directly.
|
||||
|
||||
### `defaultStore` on your machine
|
||||
|
||||
Set it once if every project you work in uses the same store. OpenSpec falls back to it when it finds no flag, no local `openspec/` folder, and no `store:` line:
|
||||
|
||||
+152
-8
@@ -50,6 +50,7 @@ Your agent runs most of these during the workflow.
|
||||
|
||||
| Command | What it does |
|
||||
|---|---|
|
||||
| [`openspec version`](#openspec-version) | Report the installed version and optionally check for an update. |
|
||||
| [`openspec feedback`](#openspec-feedback) | Submit feedback about OpenSpec. |
|
||||
| [`openspec completion`](#openspec-completion) | Install or generate shell completions. |
|
||||
|
||||
@@ -77,6 +78,21 @@ openspec init --tools none # openspec/ structure only, no tool files
|
||||
|
||||
With no `--tools`, init prompts you to pick tools in an interactive terminal. Outside one, it sets up the tools it detects in the project. With none detected it exits 1 and lists the valid ids.
|
||||
|
||||
**Store-only repositories**
|
||||
|
||||
When `openspec/config.yaml` contains a `store:` line and the repo has no local specs or changes, run init from the repository root:
|
||||
|
||||
```bash
|
||||
# install Claude Code integration files in the code repo
|
||||
openspec init --tools claude
|
||||
```
|
||||
|
||||
- **Integration files**: written in the code repo.
|
||||
- **`openspec/config.yaml`**: preserved byte-for-byte.
|
||||
- **`openspec/specs/` and `openspec/changes/`**: not created in the code repo.
|
||||
|
||||
Running init from a subdirectory exits 1 and tells you to run it from the repository root. `--language` also exits 1 because the language belongs in the external store's config. Run init in the store root or edit that config directly.
|
||||
|
||||
**Arguments**
|
||||
|
||||
| Argument | What it is |
|
||||
@@ -88,6 +104,7 @@ With no `--tools`, init prompts you to pick tools in an interactive terminal. Ou
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `--tools <tools>` | Comma-separated tool ids, `all`, or `none`. Skips the picker. Ids are listed in [Supported tools](supported-tools.md). |
|
||||
| `--language <language>` | Add a language instruction to a new project config. Rejected when the repo's `store:` line points to an external store. |
|
||||
| `--force` | Remove files from older OpenSpec layouts without asking. Interactive runs otherwise confirm the cleanup first. |
|
||||
| `--profile <profile>` | Override the global config profile for this run: `core` (the standard workflow set) or `custom` (the workflows saved in global config). |
|
||||
| `--no-animation` | Show a static welcome screen instead of the animated one. |
|
||||
@@ -117,7 +134,7 @@ Restart your IDE for the new commands to take effect.
|
||||
**Exit codes**
|
||||
|
||||
- `0`: setup completed.
|
||||
- `1`: invalid `--tools` or `--profile` value, or a non-interactive run with no tools detected and no `--tools`.
|
||||
- `1`: invalid `--tools` or `--profile` value, a non-interactive run with no tools detected and no `--tools`, or an invalid store-only invocation.
|
||||
|
||||
## openspec update
|
||||
|
||||
@@ -409,12 +426,14 @@ Config updated. Run `openspec update` in your projects to apply.
|
||||
Lists changes, or specs with `--specs`.
|
||||
|
||||
```bash
|
||||
openspec list # changes, most recently modified first
|
||||
openspec list --specs # specs with requirement counts
|
||||
openspec list --json # machine-readable, includes the resolved root
|
||||
openspec list # active changes, most recently modified first
|
||||
openspec list --archived # archived changes
|
||||
openspec list --all # active and archived changes
|
||||
openspec list --specs # specs with requirement counts
|
||||
openspec list --json # machine-readable, includes the resolved root
|
||||
```
|
||||
|
||||
Rows come from `openspec/changes/` and `openspec/specs/` under the resolved root. The `archive/` folder is skipped.
|
||||
Rows come from `openspec/changes/` and `openspec/specs/` under the resolved root. The default change listing skips `openspec/changes/archive/`.
|
||||
|
||||
**Options**
|
||||
|
||||
@@ -422,6 +441,8 @@ Rows come from `openspec/changes/` and `openspec/specs/` under the resolved root
|
||||
|---|---|
|
||||
| `--specs` | List specs instead of changes. |
|
||||
| `--changes` | List changes. This is the default. |
|
||||
| `--archived` | List only archived changes. Can't be combined with `--specs`. |
|
||||
| `--all` | List active and archived changes. Can't be combined with `--specs`. Takes precedence over `--archived`. |
|
||||
| `--sort <order>` | `recent` (last modified first) or `name`. Default: `recent`. Specs always sort by name. |
|
||||
| `--json` | Print JSON instead of the table. |
|
||||
| `--store <id>` | Use a registered store as the OpenSpec root instead of the current project. |
|
||||
@@ -435,6 +456,16 @@ Changes:
|
||||
add-rate-limit No tasks just now
|
||||
```
|
||||
|
||||
`--all` groups active and archived changes under separate headings. Each group uses the selected sort order:
|
||||
|
||||
```
|
||||
Changes:
|
||||
add-rate-limit No tasks just now
|
||||
|
||||
Archived Changes:
|
||||
2026-08-10-add-login ✓ Complete 2d ago
|
||||
```
|
||||
|
||||
```
|
||||
Specs:
|
||||
api requirements 1
|
||||
@@ -460,7 +491,9 @@ Specs:
|
||||
}
|
||||
```
|
||||
|
||||
An empty listing prints `No active changes found.` or `No specs found.` and still exits 0.
|
||||
With `--archived` or `--all`, every change object includes an `archived` boolean. The combined array uses the selected sort order. An archived change can still have an `in-progress` status when its tracked task file has unchecked tasks. Without either flag, the JSON shape stays unchanged.
|
||||
|
||||
An empty listing prints `No active changes found.`, `No archived changes found.`, `No changes found.`, or `No specs found.` and still exits 0.
|
||||
|
||||
A change is a directory directly under `openspec/changes/`. Unlike specs, changes cannot be nested in a namespace folder. A folder like `changes/mobile/` that only wraps a change (`changes/mobile/refresh-token/`) is listed with the status `not a change`, followed by a warning that names the nested directories. `--json` marks that entry with a `nested` array and adds a top-level `warnings` array. `show`, `status`, `validate` and `archive` refuse the folder with the same message. To fix it, move the change up and fold the namespace into its name:
|
||||
|
||||
@@ -541,9 +574,11 @@ A change with `--json` is delta-shaped:
|
||||
"operation": "ADDED",
|
||||
"description": "Add requirement: The API SHALL limit each client to 100 requests per minute.",
|
||||
"requirement": {
|
||||
"name": "Rate limit",
|
||||
"text": "The API SHALL limit each client to 100 requests per minute.",
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "Client exceeds the limit",
|
||||
"rawText": "- **WHEN** a client sends its 101st request within a minute\n- **THEN** the API responds 429"
|
||||
}
|
||||
]
|
||||
@@ -558,9 +593,11 @@ A change with `--json` is delta-shaped:
|
||||
}
|
||||
```
|
||||
|
||||
Each requirement carries its `name`, the header text after `Requirement:`. This is the name archive matches MODIFIED, REMOVED and RENAMED entries against. Each scenario carries its `name`, the header text after `Scenario:`. A closing `#` run on either header is not part of the name.
|
||||
|
||||
`--json --diff` keeps this top-level shape. A MODIFIED delta gains a `diff` string, a `warning` string, or both. Other operations are unchanged. An empty `diff` string means the main and delta blocks are textually identical.
|
||||
|
||||
A spec with `--json` lists its requirements with scenarios:
|
||||
A spec with `--json` lists its requirements with scenarios. Requirements and scenarios carry the same `name` fields as change JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -570,9 +607,11 @@ A spec with `--json` lists its requirements with scenarios:
|
||||
"requirementCount": 1,
|
||||
"requirements": [
|
||||
{
|
||||
"name": "Health endpoint",
|
||||
"text": "The API SHALL expose a health endpoint.",
|
||||
"scenarios": [
|
||||
{
|
||||
"name": "Health check succeeds",
|
||||
"rawText": "- **WHEN** a client requests GET /health\n- **THEN** the API responds 200"
|
||||
}
|
||||
]
|
||||
@@ -639,6 +678,21 @@ Use openspec list --changes or openspec list --specs for detailed views
|
||||
|
||||
A `Task Progress` summary line appears when any change has tasks underway.
|
||||
|
||||
Each active change also shows its schema and artifact states below its task progress bar:
|
||||
|
||||
```text
|
||||
└─ [spec-driven] proposal✓ specs→ design→ tasks✓
|
||||
```
|
||||
|
||||
| Marker | Artifact state |
|
||||
|---|---|
|
||||
| `✓` | Its output exists. An existing tasks artifact is done even when its checklist is unfinished. |
|
||||
| `→` | It is ready to create. |
|
||||
| No marker | It is blocked by a missing dependency. |
|
||||
| `(skipped)` | The change skips it. |
|
||||
|
||||
If a workflow cannot be loaded, view prints a warning and keeps that change's task progress visible. Run `openspec status --change <name>` to inspect the workflow separately. After `openspec view --store <id>`, pass the same `--store <id>` to status.
|
||||
|
||||
**Exit codes**
|
||||
|
||||
- `0`: dashboard printed.
|
||||
@@ -1288,7 +1342,11 @@ With `--json`, each form returns one object. The artifact form starts:
|
||||
...
|
||||
```
|
||||
|
||||
and continues with `outputPath`, `existingOutputPaths`, the full `instruction` and `template` strings, `dependencies`, `unlocks`, and `root`. The `apply` form carries `contextFiles`, `progress`, `tasks`, `state` (`blocked`, `ready`, `all_done`), and `instruction`.
|
||||
and continues with `outputPath`, `existingOutputPaths`, the full `instruction` and `template` strings, `dependencies`, `unlocks`, and `root`. The `apply` form carries `contextFiles`, `progress`, `tasks`, `taskTrackingConfigured`, `state` (`blocked`, `ready`, `all_done`), and `instruction`.
|
||||
|
||||
Each `tasks` entry carries `id`, `description`, `done`, `sourcePath`, and `line`. `sourcePath` is the absolute path to the tracked file that supplied the task. `line` is the task checkbox's one-based line number in that file.
|
||||
|
||||
`taskTrackingConfigured` is always a boolean: `true` when the schema sets a non-null [`apply.tracks`](schemas/schema-yaml.md#tracks), even if no file matches, and `false` otherwise. If a matched tracking file cannot be read, `unavailableTrackingFiles` contains its absolute `path` and error `reason`. This field is omitted when every matched file is readable. Readable files still contribute to `tasks` and `progress`, but `state` cannot be `all_done` until every matched file is read.
|
||||
|
||||
**Exit codes**
|
||||
|
||||
@@ -2135,6 +2193,92 @@ In an interactive terminal, remove shows the workset and asks you to confirm. Wi
|
||||
Removed workset 'checkout'. Member folders were not touched.
|
||||
```
|
||||
|
||||
## openspec version
|
||||
|
||||
Reports the running OpenSpec version and how this copy was installed.
|
||||
|
||||
```bash
|
||||
openspec version # local version and install details
|
||||
openspec version --json # structured local report
|
||||
openspec version --check # also check the registry for an update
|
||||
openspec version --check --json # structured local and update report
|
||||
```
|
||||
|
||||
Without `--check`, this command is local and does not contact a registry. It works outside an OpenSpec project. The existing `openspec --version` flag remains the shortest form and prints only the bare version number.
|
||||
|
||||
**Options**
|
||||
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `--json` | Print one versioned JSON document instead of text. |
|
||||
| `--check` | Check the configured registry for a newer release. |
|
||||
|
||||
**Output**
|
||||
|
||||
For a global npm install:
|
||||
|
||||
```text
|
||||
OpenSpec 1.13.2 (npm, global)
|
||||
```
|
||||
|
||||
The install scope is `global`, `project`, `temporary` for an ephemeral runner such as npx, or `source` for a checkout. OpenSpec omits details it cannot identify instead of guessing.
|
||||
|
||||
`--json` keeps unknown details as explicit `null` values:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"version": "1.13.2",
|
||||
"install": {
|
||||
"location": "/opt/homebrew/lib/node_modules/@fission-ai/openspec",
|
||||
"packageManager": "npm",
|
||||
"scope": "global"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With `--check`, an available update adds the latest version and a command when OpenSpec can identify a safe command for that install:
|
||||
|
||||
```text
|
||||
OpenSpec 1.13.2 (npm, global)
|
||||
Update available: 1.14.0
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"version": "1.13.2",
|
||||
"install": {
|
||||
"location": "/opt/homebrew/lib/node_modules/@fission-ai/openspec",
|
||||
"packageManager": "npm",
|
||||
"scope": "global"
|
||||
},
|
||||
"update": {
|
||||
"status": "available",
|
||||
"latest": "1.14.0",
|
||||
"command": "npm install -g @fission-ai/openspec@latest",
|
||||
"canSelfUpgrade": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Update status values:
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| `available` | The registry returned a safe version newer than the running version. |
|
||||
| `current` | The check completed and found no newer version. |
|
||||
| `disabled` | An existing privacy or update-check setting blocked registry access. `latest` is `null`. |
|
||||
| `offline` | The registry was unavailable or returned an unusable response. `latest` is `null`. |
|
||||
|
||||
`DO_NOT_TRACK`, telemetry opt-outs, `OPENSPEC_NO_UPDATE_CHECK`, CI detection, and rejected non-HTTPS registry overrides disable the check. Disabled and offline checks still exit 0 because update availability is advisory. This command never upgrades OpenSpec; `canSelfUpgrade` only reports whether the existing `openspec update` path could safely upgrade this copy.
|
||||
|
||||
**Exit codes**
|
||||
|
||||
- `0`: the local report printed, including disabled or offline update checks.
|
||||
- `1`: command syntax was invalid, such as the unsupported `--upgrade` option.
|
||||
|
||||
## openspec feedback
|
||||
|
||||
Submits feedback about OpenSpec.
|
||||
|
||||
@@ -20,7 +20,7 @@ Each change keeps its metadata at `openspec/changes/<change-name>/.openspec.yaml
|
||||
|
||||
### schema
|
||||
|
||||
The workflow schema this change follows. It is set when the change is created and wins over the project config, so a change keeps its schema even if `openspec/config.yaml` changes afterwards. Valid names are listed in [Schemas](../schemas/index.md).
|
||||
The workflow schema this change follows. It is set when the change is created and wins over the project config, so a change keeps its schema even if `openspec/config.yaml` changes afterwards. Run [`openspec schemas`](../cli.md#openspec-schemas) to list the available schema names.
|
||||
|
||||
### initiative
|
||||
|
||||
@@ -36,11 +36,11 @@ Keys other than `store` and `id` are rejected. No command reads the link today.
|
||||
|
||||
### skip_specs
|
||||
|
||||
Declares the change intentionally makes no spec deltas: a pure refactor, tooling, or docs change. With it set, validation accepts zero deltas, and artifacts that would generate spec files count as complete. Setting it while spec files exist under specs/ is a validation error. Its effect on deltas and archive is on [spec-driven](../schemas/spec-driven/index.md).
|
||||
Declares the change intentionally makes no spec deltas: a pure refactor, tooling, or docs change. With it set, validation accepts zero deltas, and artifacts that would generate spec files count as complete. Setting it while spec files exist under specs/ is a validation error.
|
||||
|
||||
### retire_capabilities
|
||||
|
||||
Authorizes archive to retire a capability. When this change's REMOVED deltas take away the last requirement a capability has, archive deletes that capability's main spec instead of stopping. The flag exists because the deletion is only recoverable from git, so it stays the author's call. The archive behavior is on [spec-driven](../schemas/spec-driven/index.md).
|
||||
Authorizes archive to retire a capability. When this change's REMOVED deltas take away the last requirement a capability has, archive deletes that capability's main spec instead of stopping. The flag exists because the deletion is only recoverable from git, so it stays the author's call.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -59,4 +59,7 @@ affected_areas:
|
||||
|
||||
The file is validated whenever a command writes or reads it. A write that fails validation throws and writes nothing. Reading an existing file fails on invalid YAML, a field that breaks its contract, or a schema name that is not available. A missing file is not an error, and the change is treated as having no metadata.
|
||||
|
||||
Unlike [config.yaml](config-yaml.md), bad values are never dropped with a warning. A metadata error stops the command. The one exception is unknown top-level keys, which are ignored rather than rejected.
|
||||
Unlike [config.yaml](config-yaml.md), bad values are never dropped with a warning. A metadata error stops the command.
|
||||
|
||||
- **Unknown top-level keys**: OpenSpec ignores them. `status`, `instructions`, `validate`, and `archive` report that they have no effect. JSON output carries the warning in its structured result.
|
||||
- **Strict validation**: `openspec validate --strict` treats an unknown-key warning as a failure.
|
||||
|
||||
@@ -15,8 +15,8 @@ The CLI keeps its machine-level settings at `~/.config/openspec/config.json` on
|
||||
| `workflows` | list of strings | No | The workflow list a `custom` profile installs |
|
||||
| `featureFlags` | map: flag → boolean | No | Boolean feature toggles |
|
||||
| `defaultStore` | string | No | Machine-level fallback store for root resolution |
|
||||
| `openers` | list | No | The tools worksets open in, and how each is launched |
|
||||
| `telemetry` | map | No | State the CLI keeps: anonymous id and notice-seen |
|
||||
| `openers` | map: tool id → settings | No | The tools worksets open in, and how each is launched |
|
||||
| `telemetry` | map | No | Telemetry opt-out, anonymous id, and notice-seen state |
|
||||
|
||||
### profile
|
||||
|
||||
@@ -40,11 +40,45 @@ The machine-level fallback store id for root resolution, consulted only when no
|
||||
|
||||
### openers
|
||||
|
||||
The tools a workset can open in, and how each is launched. Entries are hand-edited and validated on use. Each may set `style` (`workspace-file` or `attach-dirs`), `label`, `command`, `args`, and `attach_flag`, and is merged over the built-in defaults.
|
||||
The tools a workset can open in, keyed by tool id. Edit `openers` in the global `config.json` with `openspec config edit` in your terminal.
|
||||
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `style` | `workspace-file` or `attach-dirs`. Required for a new tool; optional for a built-in. |
|
||||
| `label` | Non-empty string shown in the tool picker. Defaults to the id for a new tool. |
|
||||
| `command` | Non-empty executable name or path. Defaults to the id for a new tool. Put arguments in `args`, not in this string. |
|
||||
| `args` | Array of strings passed before the workspace file or attach flags. Defaults to `[]` for a new tool. |
|
||||
| `attach_flag` | Non-empty string paired with each member path for `attach-dirs`. Defaults to `--add-dir` for a new tool. Ignored for `workspace-file`. |
|
||||
|
||||
**Built-in overrides:** `code`, `cursor`, `claude`, and `codex` retain any fields you omit. Setting `args` replaces the entire argument list; `[]` clears it.
|
||||
|
||||
**Launch styles:** `workspace-file` passes the generated `.code-workspace` path to the executable. `attach-dirs` passes one flag/path pair per member, including the primary member.
|
||||
|
||||
**Availability:** `attach-dirs` openers, including Claude Code and Codex, are disabled by default. You cannot select or save them with `--tool`, and OpenSpec refuses to open a workset that already names one. Configuration overrides do not enable the `attach-dirs` launch style.
|
||||
|
||||
**Validation:** unknown fields, invalid types, and a new tool without `style` fail when a workset command reads the opener table.
|
||||
|
||||
This example adds VS Code Insiders and passes `--new-window` whenever the built-in VS Code opener launches:
|
||||
|
||||
```json
|
||||
{
|
||||
"openers": {
|
||||
"code-insiders": {
|
||||
"style": "workspace-file",
|
||||
"label": "VS Code Insiders"
|
||||
},
|
||||
"code": {
|
||||
"args": ["--new-window"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The corresponding `code-insiders` or `code` executable must be installed and available on `PATH`.
|
||||
|
||||
### telemetry
|
||||
|
||||
State the CLI writes for telemetry: your anonymous id and whether the first-run notice was shown. It is not the opt-out. Disabling telemetry is an environment variable, on [Environment variables](environment-variables.md).
|
||||
The CLI stores your anonymous id and whether the first-run notice was shown. Set `telemetry.enabled` to `false` to disable telemetry. You can also opt out with `OPENSPEC_TELEMETRY=0` or `DO_NOT_TRACK=1` in your environment.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Each OpenSpec project keeps its config file at `openspec/config.yaml`, in the pr
|
||||
| Key | Type | Required | Effect |
|
||||
| --- | --- | --- | --- |
|
||||
| `schema` | string | Yes | The workflow schema this project's changes follow |
|
||||
| `context` | string | No | Injected into every artifact's instructions |
|
||||
| `context` | string | No | Injected into every artifact, apply, and archive |
|
||||
| `rules` | map: artifact ID → list of strings | No | Extra rules added to one artifact's built-in guidance |
|
||||
| `operations` | map: operation → guidance list | No | Advisory guidance for apply and archive work |
|
||||
| `store` | string | No | Fallback OpenSpec root when this openspec/ is config-only |
|
||||
@@ -23,11 +23,11 @@ What to write in these fields is covered in [Project configuration](../../custom
|
||||
|
||||
### schema
|
||||
|
||||
The workflow schema every change in this project follows. Valid values are `spec-driven` or a schema name the project defines. The names are listed in [Schemas](../schemas/index.md).
|
||||
The workflow schema every change in this project follows. Valid values are `spec-driven` or a schema name the project defines. Run [`openspec schemas`](../cli.md#openspec-schemas) to list the available schema names.
|
||||
|
||||
### context
|
||||
|
||||
Free text injected into every artifact's instructions. The limit is 50KB, and a larger value is ignored with a warning.
|
||||
Free text injected into every artifact's instructions and supplied to apply and archive. The limit is 50KB, and a larger value is ignored with a warning.
|
||||
|
||||
### rules
|
||||
|
||||
@@ -77,14 +77,13 @@ A filled-in config.yaml:
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Tech stack: TypeScript, React, Node.js
|
||||
We use conventional commits
|
||||
Domain: e-commerce platform
|
||||
Designs and tasks must cover Windows, macOS, and Linux
|
||||
Write all artifacts in Spanish
|
||||
|
||||
rules:
|
||||
proposal:
|
||||
- Keep proposals under 500 words
|
||||
- Always include a "Non-goals" section
|
||||
- Always state what is out of scope
|
||||
tasks:
|
||||
- Break tasks into chunks of max 2 hours
|
||||
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
| [Project configuration (config.yaml)](config-yaml.md) | `openspec/config.yaml` | The schema, context, and rules this project plans with |
|
||||
| [Change metadata (.openspec.yaml)](change-metadata.md) | `openspec/changes/<name>/.openspec.yaml` | The workflow schema, goal, scope, and spec exceptions for one change |
|
||||
| [CLI settings (config.json)](config-json.md) | `~/.config/openspec/config.json` (Windows varies) | How the openspec CLI behaves on your machine |
|
||||
| [Environment variables](environment-variables.md) | Your shell or CI environment | Telemetry opt-out, and where the config and data directories live |
|
||||
| Environment variables | Your shell or CI environment | Telemetry opt-out, and where the config and data directories live |
|
||||
| Stores | `~/.local/share/openspec/stores/` (Windows varies) | The registry and metadata behind multi-repo stores |
|
||||
|
||||
@@ -2,26 +2,26 @@
|
||||
|
||||
> Every OpenSpec term, one line each.
|
||||
|
||||
OpenSpec reuses words that mean something else in git, CI, and agent tooling. Each row gives the OpenSpec meaning, and the last column links to the page that teaches the term.
|
||||
OpenSpec reuses words that mean something else in git, CI, and agent tooling. Each row gives the OpenSpec meaning. The last column links to more detail where available.
|
||||
|
||||
| Term | Definition | More |
|
||||
|---|---|---|
|
||||
| **Apply** | Implement the tasks in a change proposal. Skill: `openspec-apply-change`. | [Apply a change](../guides/apply.md) |
|
||||
| **Apply** | Implement the tasks in a change proposal. Skill: `openspec-apply-change`. | [Apply a change](skills.md#openspec-apply-change) |
|
||||
| **Archive** | Complete a change proposal: merge its deltas into the main specs and move its folder to `openspec/changes/archive/`. | [Quickstart](../start/quickstart.md) |
|
||||
| **Artifact** | A planning document inside a change proposal: `proposal.md`, delta specs, `design.md`, `tasks.md`. Not a build output. | [Concepts](../guides/concepts.md) |
|
||||
| **Capability** | One behavior area of your system. Each has one spec at `openspec/specs/<capability>/spec.md`. | [Concepts](../guides/concepts.md) |
|
||||
| **Change proposal** | One unit of work: a folder under `openspec/changes/<name>/` holding its planning artifacts. Often shortened to "change". Not a git commit. | [Concepts](../guides/concepts.md) |
|
||||
| **Artifact** | A planning document inside a change proposal: `proposal.md`, delta specs, `design.md`, `tasks.md`. Not a build output. | [Artifacts](schemas/spec-driven/index.md#artifacts) |
|
||||
| **Capability** | One behavior area of your system. Each has one spec at `openspec/specs/<capability>/spec.md`. | [Capabilities](schemas/spec-driven/index.md#proposalmd) |
|
||||
| **Change proposal** | One unit of work: a folder under `openspec/changes/<name>/` holding its planning artifacts. Often shortened to "change". Not a git commit. | [Propose](../start/quickstart.md#step-2-propose) |
|
||||
| **Command** | A typed entry point for a workflow. Spelling varies per tool (`/opsx:propose`, `/opsx-propose`). The docs name workflows by skill instead. | [Supported tools](supported-tools.md) |
|
||||
| **Continue** | Create the next planning artifact for an existing change proposal. Skill: `openspec-continue-change`. | [Skills](skills.md) |
|
||||
| **Delivery** | How workflows are installed: as skills, commands, or both. | [Set up your project](../start/setup.md) |
|
||||
| **Delta spec** | A spec inside a change proposal listing only what changes, under `ADDED`, `MODIFIED`, `REMOVED`, and `RENAMED` headers. | [Delta specs](schemas/spec-driven/index.md#delta-specs-specmd) |
|
||||
| **Explore** | Think an idea through with the agent before proposing. Writes no code. Skill: `openspec-explore`. | [Explore an idea](../guides/explore.md) |
|
||||
| **Explore** | Think an idea through with the agent before proposing. Writes no code. Skill: `openspec-explore`. | [Explore an idea](skills.md#openspec-explore) |
|
||||
| **Fast-forward** | Create a change proposal with every planning artifact in one pass, ready to implement. Skill: `openspec-ff-change`. Not a git fast-forward. | [Skills](skills.md) |
|
||||
| **Legacy workflow** | The pre-OPSX `/openspec:*` commands. | [Migration](../help/legacy/migration.md) |
|
||||
| **Legacy workflow** | The pre-OPSX `/openspec:*` commands. | |
|
||||
| **Loop** | The cycle a change proposal moves through: explore, propose, review, apply, archive. | [Quickstart](../start/quickstart.md) |
|
||||
| **Main specs** | The `openspec/specs/` tree: the current, agreed behavior of your system. Archiving merges deltas into it. A capability with no spec yet gets one from its `ADDED` requirements. | [Concepts](../guides/concepts.md) |
|
||||
| **Main specs** | The `openspec/specs/` tree: the current, agreed behavior of your system. Archiving merges deltas into it. A capability with no spec yet gets one from its `ADDED` requirements. | [Archive](../start/quickstart.md#step-5-archive) |
|
||||
| **OpenSpec root** | The `openspec/` tree a command resolves to and operates on: your repo's, or a store's. | [Stores](../multi-repo/stores.md#where-artifacts-get-created-when-using-stores) |
|
||||
| **OPSX** | The current OpenSpec workflow system, and the command prefix it installs (`/opsx:`). | [Architecture](architecture/index.md) |
|
||||
| **OPSX** | The current OpenSpec workflow system, and the command prefix it installs (`/opsx:`). | |
|
||||
| **Profile** | Which workflows init installs: `core` or `custom`. | [Profiles](../customize/profiles.md) |
|
||||
| **Propose** | Create a change proposal and generate all its planning artifacts in one step. Skill: `openspec-propose`. | [Quickstart](../start/quickstart.md) |
|
||||
| **Registry** | The machine-level list of registered stores, in `registry.yaml`. Not a package registry. | [CLI](cli.md#openspec-store) |
|
||||
@@ -29,12 +29,12 @@ OpenSpec reuses words that mean something else in git, CI, and agent tooling. Ea
|
||||
| **Scenario** | A testable example under a requirement, in WHEN/THEN form. | [Delta specs](schemas/spec-driven/index.md#delta-specs-specmd) |
|
||||
| **Schema** | The definition of which artifacts a change proposal produces, and in what order. Not JSON Schema. | [Schemas](schemas/index.md) |
|
||||
| **Skill** | A workflow's instructions, installed where your AI tool reads them (`.agents/skills/`, ...). | [Skills](skills.md) |
|
||||
| **Spec** | A file describing how one capability behaves today, at `openspec/specs/<capability>/spec.md`. | [Concepts](../guides/concepts.md) |
|
||||
| **Spec** | A file describing how one capability behaves today, at `openspec/specs/<capability>/spec.md`. | [Archive](../start/quickstart.md#step-5-archive) |
|
||||
| **spec-driven** | The default schema: proposal, then delta specs, then design, then tasks. | [spec-driven](schemas/spec-driven/index.md) |
|
||||
| **Store** | A standalone OpenSpec repo registered on your machine, for planning that spans repositories. Not a data store. | [Stores (beta)](../multi-repo/stores.md) |
|
||||
| **Sync** | Merge implemented deltas into the main specs without archiving. Skill: `openspec-sync-specs`. | [Skills](skills.md) |
|
||||
| **Template** | The starting content a schema gives each artifact. | [Schemas](../customize/schemas.md) |
|
||||
| **Update** | As a skill (`openspec-update-change`): revise a change proposal's planning artifacts. As a CLI command (`openspec update`): refresh OpenSpec's installed files. | [Change course](../guides/change-course.md), [CLI](cli.md) |
|
||||
| **Update** | As a skill (`openspec-update-change`): revise a change proposal's planning artifacts. As a CLI command (`openspec update`): refresh OpenSpec's installed files. | [Update a change](skills.md#openspec-update-change), [CLI](cli.md) |
|
||||
| **Verify** | Check the implementation matches a change proposal's artifacts before archiving. Skill: `openspec-verify-change`. | [Skills](skills.md) |
|
||||
| **Workflow** | A named OpenSpec action (propose, apply, archive, ...), installed into your AI tool as a skill or command. | [Set up your project](../start/setup.md) |
|
||||
| **Workset** | A personal, local group of folders opened together in one tool. Not a store, and nothing is shared. | [Worksets (beta)](../multi-repo/worksets.md) |
|
||||
|
||||
@@ -74,7 +74,15 @@ A glob can match several files:
|
||||
generates: specs/**/*.md
|
||||
```
|
||||
|
||||
This matches Markdown files below `openspec/changes/add-auth/specs/`. OpenSpec treats a value containing `*`, `?`, or `[` as a glob.
|
||||
This matches Markdown files below `openspec/changes/add-auth/specs/`.
|
||||
|
||||
OpenSpec recognizes these glob forms in `generates`:
|
||||
|
||||
- **Wildcards and character classes**: values containing `*`, `?`, or `[`, such as `specs/**/*.md` and `review-[ab].md`.
|
||||
- **Brace expansions**: alternatives such as `review-{api,ui}.md` and ranges such as `file-{1..3}.md`.
|
||||
- **Extglobs**: patterns such as `@(proposal|design).md`, `+(proposal|design).md`, and `!(proposal|design).md`.
|
||||
|
||||
**Literal filenames**: a leading `!` alone does not make a glob. Use `generates: '!review.md'` to name that file. Plain parentheses such as `(proposal|design).md` and single-element braces such as `review-{api}.md` also remain literal.
|
||||
|
||||
OpenSpec rejects absolute paths and paths containing a `..` segment.
|
||||
|
||||
@@ -119,7 +127,7 @@ OpenSpec rejects absolute paths and paths containing a `..` segment.
|
||||
| Field | Contract |
|
||||
|---|---|
|
||||
| `requires` | **Required.** A non-empty list of artifacts that must exist before apply instructions become ready. |
|
||||
| `tracks` | An optional relative path to a Markdown task file in the change folder. Default: `null`. |
|
||||
| `tracks` | An optional relative path or glob for Markdown task files in the change folder. Default: `null`. |
|
||||
| `instruction` | Optional guidance sent to the agent when apply is ready. OpenSpec uses built-in guidance by default. |
|
||||
|
||||
Artifact `requires` controls planning order. `apply.requires` controls when apply instructions become ready.
|
||||
@@ -132,7 +140,9 @@ The path starts from the change folder. For a change named `add-auth`, `tracks:
|
||||
openspec/changes/add-auth/tasks.md
|
||||
```
|
||||
|
||||
Apply stays blocked if that file is missing or contains no checkbox with task text. OpenSpec counts these checkbox forms:
|
||||
A glob such as `tracks: "**/tasks.md"` reads every matching file, such as `backend/tasks.md` and `frontend/tasks.md`. OpenSpec combines their tasks and progress. Use the same value for an artifact's `generates` field so status and list track the same files.
|
||||
|
||||
Apply stays blocked if no file matches or the matched files contain no checkbox with task text. OpenSpec counts these checkbox forms:
|
||||
|
||||
```markdown
|
||||
- [ ] Pending task
|
||||
@@ -145,11 +155,13 @@ Apply stays blocked if that file is missing or contains no checkbox with task te
|
||||
|
||||
Any Markdown list marker works: `-`, `*`, `+`, or a number of up to nine digits followed by `.` or `)`. Leading spaces are allowed. The [tasks.md section of the spec-driven page](spec-driven/index.md#tasksmd) defines the stricter format produced by the default schema.
|
||||
|
||||
The tracked file drives the apply state:
|
||||
The tracked files drive the apply state:
|
||||
|
||||
- **`blocked`**: the file is missing, or no checkbox has task text.
|
||||
- **`ready`**: at least one tracked task is pending.
|
||||
- **`all_done`**: every tracked task is checked.
|
||||
- **`blocked`**: no file matches, or no readable file has a checkbox with task text.
|
||||
- **`ready`**: at least one task is pending, or a matched file could not be read while another provides tasks.
|
||||
- **`all_done`**: every tracked task is checked and every matched file was read.
|
||||
|
||||
If a matched file cannot be read, apply keeps the tasks and progress from readable files but does not mark the change `all_done`. [Apply JSON output](../cli.md#openspec-instructions) identifies each unavailable file and the reason.
|
||||
|
||||
OpenSpec rejects absolute paths and paths containing a `..` segment.
|
||||
|
||||
|
||||
@@ -67,9 +67,12 @@ The template the agent receives as the output format ([templates/proposal.md](ht
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
<!-- Capabilities being introduced. Use kebab-case for path segments you introduce
|
||||
(e.g., user-auth or identity/user-auth) that follow the project's existing
|
||||
spec organization. Each creates specs/<capability-path>/spec.md. -->
|
||||
<!-- Capabilities being introduced. Name each capability for a cohesive system
|
||||
behavior that can own related requirements as the system evolves. Do not name
|
||||
implementation tasks or proposal sections. Avoid broad catch-all names. Use
|
||||
kebab-case for path segments you introduce (e.g., user-auth or identity/user-auth)
|
||||
that follow the project's existing spec organization. Each creates
|
||||
specs/<capability-path>/spec.md. -->
|
||||
- `<capability-path>`: <brief description of what this capability covers>
|
||||
|
||||
### Modified Capabilities
|
||||
@@ -98,7 +101,7 @@ Sections:
|
||||
- **Why**: 1-2 sentences on the problem or opportunity. What problem does this solve? Why now?
|
||||
- **What Changes**: Bullet list of changes. Be specific about new capabilities, modifications, or removals. Mark breaking changes with **BREAKING**.
|
||||
- **Capabilities**: Identify which specs will be created or modified:
|
||||
- **New Capabilities**: List capabilities being introduced. Each becomes a new `specs/<capability-path>/spec.md`. Use kebab-case for path segments you introduce (e.g., `user-auth` or `identity/user-auth`) and follow the project's existing spec organization.
|
||||
- **New Capabilities**: List capabilities being introduced. Each becomes a new `specs/<capability-path>/spec.md`. Name each capability for a durable system behavior (for example, `user-auth`), not the work in this change (for example, `add-login-endpoint`). Choose a cohesive boundary that can own related requirements as the system evolves; avoid broad catch-all capabilities. Use kebab-case for path segments you introduce (e.g., `user-auth` or `identity/user-auth`) and follow the project's existing spec organization.
|
||||
- **Modified Capabilities**: List existing capabilities whose REQUIREMENTS are changing. Only include if spec-level behavior changes (not just implementation details). Each needs a delta spec file. Use the exact existing path under `openspec/specs/`. Leave empty if no requirement changes.
|
||||
- **Impact**: Affected code, APIs, dependencies, or systems.
|
||||
|
||||
@@ -205,6 +208,7 @@ Format requirements:
|
||||
- Each scenario: `#### Scenario: <name>` with WHEN/THEN format
|
||||
- **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently.
|
||||
- Every requirement MUST have at least one scenario.
|
||||
- Keep each requirement's description (the text between `### Requirement:` and its first scenario) to 500 characters or fewer. `openspec validate` flags longer descriptions in ADDED requirements and in the main spec. This is a warning: normal validation still passes, but `openspec validate --strict` fails on it. When writing a new requirement, state one behavior per requirement: move examples and edge cases into scenarios, and split a requirement that covers several behaviors into separate `### Requirement:` blocks, each with its own scenarios. Under MODIFIED, keep the existing requirement block whole; never split, trim or rewrite existing text just to meet the length. Split an existing long requirement only when the user asks for it, in a change made for that purpose: under MODIFIED, keep its header and every scenario and cut its description down to one behavior without changing its meaning, then add each behavior you removed as its own ADDED requirement with its own scenarios.
|
||||
|
||||
New capabilities only: the delta spec's first section is `## Purpose` -
|
||||
one or two sentences (50+ characters, or `openspec validate --strict`
|
||||
@@ -214,10 +218,16 @@ left with a `TBD ... Update Purpose after archive` placeholder to fill in
|
||||
by hand. Do NOT add `## Purpose` to a delta for an existing capability -
|
||||
that spec already has one and the delta's is ignored. To change an
|
||||
existing capability's Purpose - including a leftover `TBD` placeholder -
|
||||
edit `openspec/specs/<capability-path>/spec.md` directly.
|
||||
edit `<planningHome.root>/openspec/specs/<capability-path>/spec.md`
|
||||
directly. `planningHome.root` comes from the `openspec instructions ...
|
||||
--json` response. Always use it rather than a repo-relative path: it
|
||||
resolves to the store whenever the change lives in one - whether that
|
||||
came from `--store`, a project `store:` pointer, or a global default
|
||||
store - and to the current repository otherwise. Do not try to work out
|
||||
which case applies; the field already has.
|
||||
|
||||
MODIFIED requirements workflow:
|
||||
1. Locate the existing requirement in openspec/specs/<capability-path>/spec.md
|
||||
1. Locate the existing requirement in `<planningHome.root>/openspec/specs/<capability-path>/spec.md` (the same store-aware root as above)
|
||||
2. Copy the ENTIRE requirement block (from `### Requirement:` through all scenarios)
|
||||
3. Paste under `## MODIFIED Requirements` and edit to reflect new behavior
|
||||
4. Ensure header text matches exactly (whitespace-insensitive)
|
||||
@@ -351,17 +361,34 @@ Before writing tasks, check design.md for Open Questions. If any of them
|
||||
would change what gets built, resolve them with the user first - do not
|
||||
bake an unstated assumption into the task list.
|
||||
|
||||
**IMPORTANT: Follow the template below exactly.** The apply phase parses
|
||||
**IMPORTANT: Follow the template below for tracked tasks.** The apply phase parses
|
||||
checkbox format to track progress. A box holding only `x` counts as done,
|
||||
upper or lower case and with any spacing, so `- [ x]` is done too. Every
|
||||
other marker, including `- [~]`, `- [-]` and an empty `- []`, reads as
|
||||
unfinished. A line with no checkbox is not tracked at all.
|
||||
|
||||
Guidelines:
|
||||
- Group related tasks under ## numbered headings
|
||||
- Each task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Group related tracked tasks under ## numbered headings
|
||||
- Each tracked task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Tasks should be small enough to complete in one session
|
||||
- Order tasks by dependency (what must be done first?)
|
||||
- Track implementation and verification work that can be completed before
|
||||
archive. If the requested workflow includes archive or work that requires
|
||||
this change to be archived, preserve those steps as plain bullets in an
|
||||
optional `## Workflow follow-up` section at the end of tasks.md. These
|
||||
bullets are reference information outside tracked task progress.
|
||||
- Each task MUST state how to verify completion (a test, command,
|
||||
observable behavior, or delivered artifact). Put the verification in
|
||||
that task's checkbox description. Use a separate verification task only
|
||||
when it checks broader integration or system behavior that spans
|
||||
multiple implementation tasks.
|
||||
- Each task group MUST land the tests and documentation its own work
|
||||
calls for. Do NOT collect testing or documentation into a final group -
|
||||
when a late group first exercises work from an early one, the failures
|
||||
cascade back through every group in between and force rework. A group
|
||||
whose work calls for neither, such as scaffolding or dependency setup,
|
||||
carries neither. A final group is for integration checks only, not for
|
||||
the tests and docs an earlier group owed.
|
||||
|
||||
Example:
|
||||
```
|
||||
@@ -369,17 +396,25 @@ Example:
|
||||
|
||||
## 1. Setup
|
||||
|
||||
- [ ] 1.1 Create new module structure
|
||||
- [ ] 1.2 Add dependencies to package.json
|
||||
- [ ] 1.1 Create new module structure and verify expected files are present
|
||||
- [ ] 1.2 Add dependencies to package.json and verify package installation succeeds
|
||||
|
||||
## 2. Core Implementation
|
||||
|
||||
- [ ] 2.1 Implement data export function
|
||||
- [ ] 2.2 Add CSV formatting utilities
|
||||
- [ ] 2.1 Implement data export function and verify the export test passes
|
||||
- [ ] 2.2 Add CSV formatting utilities and verify unit tests cover quoting and delimiters
|
||||
- [ ] 2.3 Document the export API in docs/export.md and verify the documented command runs as written
|
||||
```
|
||||
|
||||
When applicable, append workflow follow-up as plain bullets, for example:
|
||||
```
|
||||
## Workflow follow-up
|
||||
|
||||
- Archive the change after the project's review requirements are satisfied.
|
||||
- Verify the archived result.
|
||||
```
|
||||
|
||||
Reference specs for what needs to be built, design for how to build it.
|
||||
Each task should be verifiable - you know when it's done.
|
||||
````
|
||||
|
||||
## Apply
|
||||
|
||||
@@ -90,7 +90,7 @@ Implement a change proposal's tasks, working through the list until done or bloc
|
||||
| Contract | Description |
|
||||
|---|---|
|
||||
| **Arguments** | A change proposal name (`add-auth`), optional. If the target is ambiguous it lists the active change proposals and asks you to pick. |
|
||||
| **Creates** | Code: the minimal changes each task calls for, in your project files. In the change proposal it touches only the tasks file, checking off each finished task (`- [ ]` to `- [x]`). |
|
||||
| **Creates** | Code: the minimal changes each task calls for, in your project files. In the change proposal it checks off each finished task (`- [ ]` to `- [x]`) in the tracked file identified by `sourcePath` and one-based `line`. A schema may track tasks across multiple files. |
|
||||
| **Response** | Progress per task, then an overall count (N/M tasks complete). All done: suggests `openspec-archive-change`. Blocked by missing artifacts: points to `openspec-continue-change`, or to `openspec status` and `openspec instructions` when that skill is not installed (the core profile leaves it out). Unclear tasks or errors: pauses and asks. |
|
||||
|
||||
## openspec-update-change
|
||||
@@ -101,7 +101,7 @@ other.
|
||||
| Contract | Description |
|
||||
|---|---|
|
||||
| **Arguments** | A change proposal name, optional, plus the revision you want. With no revision stated it runs a coherence review: artifacts checked against each other for contradictions, gaps, and duplication. |
|
||||
| **Creates** | Nothing new. Edits only artifact files that already exist. Missing artifacts are `openspec-continue-change`'s job. Without that skill (the core profile leaves it out), it points to `openspec status` and `openspec instructions` instead. Never code. |
|
||||
| **Creates** | Edits artifact files that already exist. One exception: for an artifact written as a glob, such as `specs/**/*.md`, that already has at least one file, it can add a missing companion file once you confirm the path. An artifact with no files yet is `openspec-continue-change`'s job. Without that skill (the core profile leaves it out), it points to `openspec status` and `openspec instructions` instead. Never code. |
|
||||
| **Response** | Shows each proposed revision and writes it only after you confirm, one artifact at a time. Ends with what was revised and the next step; implementation waits for `openspec-apply-change`. |
|
||||
|
||||
## openspec-sync-specs
|
||||
@@ -121,7 +121,7 @@ Move a finished change proposal to the archive.
|
||||
| Contract | Description |
|
||||
|---|---|
|
||||
| **Arguments** | A change proposal name, optional. |
|
||||
| **Creates** | Moves the change proposal folder to `openspec/changes/archive/YYYY-MM-DD-<name>/` (no date added if the name already starts with one). With your approval it first syncs outstanding delta specs via `openspec-sync-specs`. Never code. |
|
||||
| **Creates** | Moves the change proposal folder to `openspec/changes/archive/YYYY-MM-DD-<name>/` (no date added if the name already starts with one). With your approval it first syncs outstanding delta specs. When `openspec-sync-specs` is installed, it runs that workflow. Otherwise, it merges the delta specs into the main specs itself. Never code. |
|
||||
| **Response** | Warns and asks before archiving with incomplete artifacts or tasks, and asks whether to sync when delta specs exist. Ends with a summary: name, schema, archive location, spec sync status, and any warnings. |
|
||||
|
||||
## openspec-new-change
|
||||
|
||||
@@ -15,27 +15,35 @@ The id goes to `openspec init --tools <id>` to skip the picker ([CLI](cli.md)).
|
||||
| Tool | `--tools` id | Skills | Skill invocation | Commands | Command invocation |
|
||||
|---|---|---|---|---|---|
|
||||
| Amazon Q Developer | `amazon-q` | `.amazonq/skills/` | `/openspec-apply-change` | `.amazonq/prompts/` | `@opsx-apply` |
|
||||
| Amp | `amp` | `.agents/skills/` | `/openspec-apply-change` | none | none |
|
||||
| Antigravity | `antigravity` | `.agents/skills/` | `/openspec-apply-change` | `.agents/workflows/` | `/opsx-apply` |
|
||||
| AtomCode | `atomcode` | `.atomcode/skills/` | `/openspec-apply-change` | `.atomcode/commands/` | `/opsx-apply` |
|
||||
| Auggie (Augment CLI) | `auggie` | `.augment/skills/` | `/openspec-apply-change` | `.augment/commands/` | `/opsx-apply` |
|
||||
| Bob Shell | `bob` | `.bob/skills/` | `/openspec-apply-change` | `.bob/commands/` | `/opsx-apply` |
|
||||
| IBM Bob | `bob` | `.bob/skills/` | `/openspec-apply-change` | `.bob/commands/` | `/opsx-apply` |
|
||||
| Claude Code | `claude` | `.claude/skills/` | `/openspec-apply-change` | `.claude/commands/opsx/` | `/opsx:apply` |
|
||||
| Cline | `cline` | `.cline/skills/` | `/openspec-apply-change` | `.clinerules/workflows/` | `/opsx-apply` |
|
||||
| CodeArts | `codeartsagent` | `.codeartsdoer/skills/` | `/openspec-apply-change` | none | none |
|
||||
| CodeBuddy Code (CLI) | `codebuddy` | `.codebuddy/skills/` | `/openspec-apply-change` | `.codebuddy/commands/opsx/` | `/opsx:apply` |
|
||||
| Code Studio | `codestudio` | `.codestudio/skills/` | `/openspec-apply-change` | `.codestudio/prompts/` | `/opsx-apply` |
|
||||
| Codex | `codex` | `.agents/skills/` | `$openspec-apply-change` | none | none |
|
||||
| Continue | `continue` | `.continue/skills/` | `/openspec-apply-change` | `.continue/prompts/` | `/opsx-apply` |
|
||||
| CoStrict | `costrict` | `.cospec/skills/` | `/openspec-apply-change` | `.cospec/openspec/commands/` | `/opsx-apply` |
|
||||
| Crush | `crush` | `.crush/skills/` | `/openspec-apply-change` | `.crush/commands/opsx/` | `/opsx:apply` |
|
||||
| Cursor | `cursor` | `.cursor/skills/` | `/openspec-apply-change` | `.cursor/commands/` | `/opsx-apply` |
|
||||
| DeepSeek Harness | `dsh` | `.dsh/skills/` | `/openspec-apply-change` | none | none |
|
||||
| Devin Desktop (formerly Windsurf) | `devin` | `.devin/skills/` | `/openspec-apply-change` | `.devin/workflows/` | `/opsx-apply` |
|
||||
| EasyCode | `easycode` | `.easycode/skills/` | `/openspec-apply-change` | `.easycode/commands/opsx/` | `/opsx:apply` |
|
||||
| Factory Droid | `factory` | `.factory/skills/` | `/openspec-apply-change` | `.factory/commands/` | `/opsx-apply` |
|
||||
| ForgeCode | `forgecode` | `.forge/skills/` | `/openspec-apply-change` | none | none |
|
||||
| Gemini CLI | `gemini` | `.gemini/skills/` | `/openspec-apply-change` | `.gemini/commands/opsx/` | `/opsx:apply` |
|
||||
| GigaCode | `gigacode` | `.gigacode/skills/` | `/openspec-apply-change` | `.gigacode/commands/` | `/opsx-apply` |
|
||||
| GitHub Copilot | `github-copilot` | `.github/skills/` | `/openspec-apply-change` | `.github/prompts/` | `/opsx-apply` |
|
||||
| Grok Build | `grok` | `.grok/skills/` | `/openspec-apply-change` | none | none |
|
||||
| GSD | `gsd` | `.agents/skills/` | ask for `openspec-apply-change` | none | none |
|
||||
| Hermes Agent | `hermes` | `.hermes/skills/` | `/openspec-apply-change` | none | none |
|
||||
| iFlow | `iflow` | `.iflow/skills/` | `/openspec-apply-change` | `.iflow/commands/` | `/opsx-apply` |
|
||||
| Junie | `junie` | `.junie/skills/` | `/openspec-apply-change` | `.junie/commands/` | `/opsx-apply` |
|
||||
| Kilo Code | `kilocode` | `.kilocode/skills/` | `/openspec-apply-change` | `.kilocode/workflows/` | `/opsx-apply` |
|
||||
| Kilo Code | `kilocode` | `.kilocode/skills/` | `/openspec-apply-change` | `.kilo/command/` | `/opsx-apply` |
|
||||
| Kimi Code | `kimi` | `.kimi-code/skills/` | `/skill:openspec-apply-change` | none | none |
|
||||
| Kiro | `kiro` | `.kiro/skills/` | `/openspec-apply-change` | `.kiro/prompts/` | `/opsx-apply` |
|
||||
| Lingma | `lingma` | `.lingma/skills/` | `/openspec-apply-change` | `.lingma/commands/opsx/` | `/opsx:apply` |
|
||||
@@ -47,6 +55,8 @@ The id goes to `openspec init --tools <id>` to skip the picker ([CLI](cli.md)).
|
||||
| Qoder | `qoder` | `.qoder/skills/` | `/openspec-apply-change` | `.qoder/commands/opsx/` | `/opsx:apply` |
|
||||
| Qwen Code | `qwen` | `.qwen/skills/` | `/openspec-apply-change` | `.qwen/commands/` | `/opsx-apply` |
|
||||
| Trae | `trae` | `.trae/skills/` | `/openspec-apply-change` | `.trae/commands/` | `/opsx-apply` |
|
||||
| [Veai](https://veai.ru/docs/veai/download) | `veai` | `.veai/skills/` | `/openspec-apply-change` | none | none |
|
||||
| Warp | `warp` | `.warp/skills/` | `/openspec-apply-change` | none | none |
|
||||
| ZCode | `zcode` | `.zcode/skills/` | `/openspec-apply-change` | `.zcode/commands/opsx/` | `/opsx:apply` |
|
||||
| Zoo Code | `roocode` | `.roo/skills/` | `/openspec-apply-change` | `.roo/commands/` | `/opsx-apply` |
|
||||
| Other / Universal | `agents` | `.agents/skills/` | `/openspec-apply-change` | none | none |
|
||||
@@ -54,14 +64,21 @@ The id goes to `openspec init --tools <id>` to skip the picker ([CLI](cli.md)).
|
||||
- **Skill invocation**: whether a tool registers skills as typed entries is the tool's
|
||||
own behavior. The column shows the spelling OpenSpec uses in generated files and in
|
||||
the hint init prints. Check your tool's docs if typing it does nothing.
|
||||
- **Command file formats**: most tools take `.md` command files. Gemini CLI takes
|
||||
`.toml`, Continue `.prompt`, Kiro and GitHub Copilot `.prompt.md`. The spelling you
|
||||
type is the same either way.
|
||||
- **Command file formats**: most tools take `.md` command files. EasyCode and Gemini
|
||||
CLI take `.toml`, Continue `.prompt`, and Code Studio, Kiro, and GitHub Copilot
|
||||
`.prompt.md`. The spelling you type is the same either way.
|
||||
|
||||
## Per-tool notes
|
||||
|
||||
A tool not listed here behaves exactly as its row reads.
|
||||
|
||||
### Amp
|
||||
|
||||
- **Project skills**: Amp reads OpenSpec skills from `.agents/skills/`.
|
||||
- **No command files**: Amp runs skills directly, so init skips command generation.
|
||||
- **Shared folder**: Amp shares `.agents/skills/` with Antigravity, Codex, Zed Agent,
|
||||
and the `agents` target. OpenSpec writes the skill tree once.
|
||||
|
||||
### Antigravity
|
||||
|
||||
- **Current folder**: Antigravity v1.20.5 and later read workspace skills and
|
||||
@@ -69,8 +86,8 @@ A tool not listed here behaves exactly as its row reads.
|
||||
- **Legacy folder**: after OpenSpec writes replacements, it removes equivalent
|
||||
generated files from `.agent/`. Custom files and changed generated files stay in
|
||||
`.agent/` for you to review.
|
||||
- **Shared skills**: Antigravity shares `.agents/skills/` with Codex, Zed Agent, and
|
||||
the `agents` target. OpenSpec writes that skill tree once while still writing
|
||||
- **Shared skills**: Antigravity shares `.agents/skills/` with Amp, Codex, Zed Agent,
|
||||
and the `agents` target. OpenSpec writes that skill tree once while still writing
|
||||
Antigravity commands to `.agents/workflows/`.
|
||||
|
||||
### Cline
|
||||
@@ -80,17 +97,34 @@ Skills stay in `.cline/skills/`.
|
||||
|
||||
### Codex
|
||||
|
||||
- **Invocation**: type `$openspec-<skill>`. Codex does not recognize the
|
||||
`/openspec-<skill>` form ([upstream issue](https://github.com/openai/codex/issues/11817)).
|
||||
- **CLI and IDE extension**: mention `$openspec-propose` with your idea, or run
|
||||
`/skills` to select the skill. Codex does not recognize `/openspec-propose`
|
||||
([upstream issue](https://github.com/openai/codex/issues/11817)).
|
||||
- **Desktop app**: open Skills in the sidebar and select `openspec-propose`.
|
||||
[OpenAI's skills documentation](https://learn.chatgpt.com/docs/build-skills)
|
||||
describes both interfaces.
|
||||
- **No command files**: Codex runs skills directly, so init skips commands even when
|
||||
delivery includes them and prints `Commands skipped for: codex (uses skills)`.
|
||||
- **Shared folder**: Codex skills land in `.agents/skills/`, the same tree Antigravity,
|
||||
Zed Agent, and the `agents` target use. Selecting more than one keeps a single
|
||||
compatible tree, and its handoffs spell both `$openspec-*` and `/openspec-*` when
|
||||
Codex owns it.
|
||||
- **Shared folder**: Codex skills land in `.agents/skills/`, the same tree Amp,
|
||||
Antigravity, Zed Agent, and the `agents` target use. Selecting more than one keeps a
|
||||
single compatible tree, and its handoffs spell both `$openspec-*` and `/openspec-*`
|
||||
when Codex owns it.
|
||||
- **Legacy path**: skills installed under `.codex/skills/` by older versions are
|
||||
migrated on the next `openspec update`.
|
||||
|
||||
### DeepSeek Harness
|
||||
|
||||
- **Project root**: DSH uses the nearest `.git` ancestor, or the current directory
|
||||
outside Git. Run `openspec init --tools dsh` there. For a nested OpenSpec project,
|
||||
add the absolute path to its `.dsh/skills/` directory to DSH's `customSkillDirs`.
|
||||
Git-root skills still win if names overlap
|
||||
([upstream discovery rules](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/skill/skill-filesystem)).
|
||||
- **Priority**: `.dsh/skills/` takes precedence over same-named skills in
|
||||
`.agents/skills/`.
|
||||
- **Delivery**: use `skills` or `both`. With `commands`, no DSH workflows are
|
||||
installed. Change delivery with `openspec config profile`, then rerun
|
||||
`openspec init --tools dsh`.
|
||||
|
||||
### Devin Desktop (formerly Windsurf)
|
||||
|
||||
- **Two agents**: command files in `.devin/workflows/` work only in Devin Desktop.
|
||||
@@ -102,8 +136,22 @@ Skills stay in `.cline/skills/`.
|
||||
|
||||
### GitHub Copilot
|
||||
|
||||
Prompt files register as slash commands in the Copilot IDE extensions (VS Code,
|
||||
JetBrains, Visual Studio). Copilot CLI does not read `.github/prompts/`.
|
||||
- **IDE extensions (command delivery)**: VS Code, JetBrains, and Visual Studio load
|
||||
`.github/prompts/opsx-<id>.prompt.md` as `/opsx-<id>`. If a command disappears
|
||||
while its file still exists, restart the IDE.
|
||||
- **Copilot CLI (skill delivery)**: the CLI ignores `.github/prompts/` and loads
|
||||
`.github/skills/openspec-*/SKILL.md` instead. Invoke a skill as
|
||||
`/openspec-<skill>`. If a skill disappears while its file still exists, run
|
||||
`/skills reload`, then `/skills info openspec-propose` to confirm discovery.
|
||||
|
||||
### GSD
|
||||
|
||||
- **Project skills**: GSD reads OpenSpec workflows from
|
||||
[`.agents/skills/`](https://github.com/open-gsd/gsd-pi/blob/main/docs/user-docs/skills.md).
|
||||
- **Invocation**: ask GSD to use the `openspec-<workflow>` skill. GSD can also select
|
||||
a matching skill through its skill discovery setting.
|
||||
- **No subagent files**: [`.gsd/agents/`](https://github.com/open-gsd/gsd-pi/blob/main/docs/user-docs/subagents.md)
|
||||
contains GSD subagent definitions. OpenSpec does not write workflow skills there.
|
||||
|
||||
### Hermes Agent
|
||||
|
||||
@@ -118,6 +166,13 @@ init prints this reminder after install.
|
||||
- **Safe across projects**: a commands-only delivery leaves the global skills in
|
||||
place, so one project's setting cannot remove skills another project uses.
|
||||
|
||||
### Warp
|
||||
|
||||
- **Skills always**: skills go to `.warp/skills/` even when delivery is `commands`,
|
||||
because Warp has no command files and invokes skills directly.
|
||||
- **What OpenSpec claims**: only `.warp/skills/`. Warp settings and `WARP.md` are
|
||||
not created or edited.
|
||||
|
||||
### Other / Universal (shared `.agents` skills)
|
||||
|
||||
- **When it fits**: any tool that reads the shared `.agents/skills/` folder,
|
||||
@@ -125,7 +180,7 @@ init prints this reminder after install.
|
||||
assistant is not listed. The init picker's search box finds it by `universal`,
|
||||
`other`, `generic`, `custom`, `proprietary`, `unlisted`, `unsupported`,
|
||||
`vendor-neutral`, or `agents.md`.
|
||||
- **Alongside other targets**: Antigravity, Codex, Zed Agent, and this target share
|
||||
- **Alongside other targets**: Amp, Antigravity, Codex, Zed Agent, and this target share
|
||||
one physical skill tree. OpenSpec records one writer in `.openspec-target` and
|
||||
writes the tree once per run. Each tool's separate command files are still
|
||||
generated.
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
OpenSpec is a Node.js CLI. You need version 20.19.0 or newer.
|
||||
OpenSpec runs on Node.js 20.19.0 or newer. Homebrew installs Node.js as a
|
||||
dependency, and the Nix package includes the runtime. Check your installed version
|
||||
before using another install method.
|
||||
|
||||
In your terminal:
|
||||
|
||||
@@ -13,7 +15,9 @@ In your terminal:
|
||||
node --version
|
||||
```
|
||||
|
||||
If that prints `v20.19.0` or higher, you're set. If not, install a newer Node from [nodejs.org](https://nodejs.org) or through your version manager (nvm, fnm, asdf, volta).
|
||||
If that prints `v20.19.0` or higher, you're set. If not, install a newer Node from
|
||||
[nodejs.org](https://nodejs.org) or through your version manager (nvm, fnm, asdf,
|
||||
volta). You can skip this check when you install with Homebrew or Nix.
|
||||
|
||||
The workflow itself runs inside an AI coding tool: Claude Code, Cursor, or any other tool on the [supported list](../reference/supported-tools.md).
|
||||
|
||||
@@ -53,6 +57,16 @@ In your terminal:
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
Homebrew installs OpenSpec and its Node.js dependency on macOS or Linux. In your terminal:
|
||||
|
||||
```bash
|
||||
brew install openspec
|
||||
```
|
||||
|
||||
The formula is published in [homebrew-core](https://formulae.brew.sh/formula/openspec), so you don't need to add a tap.
|
||||
|
||||
### Yarn
|
||||
|
||||
`yarn global add` is Yarn Classic (1.x) only. Modern Yarn removed global installs, so use npm, pnpm, or bun instead. A global CLI doesn't have to share your project's package manager.
|
||||
@@ -123,7 +137,7 @@ When a newer CLI is out, [`openspec update`](../reference/cli.md#openspec-update
|
||||
|
||||
|
||||
> [!WARNING]
|
||||
> On Deno, re-run the [Deno install](#deno) with `-f`; it won't overwrite the installed command without it. On Nix, use `nix profile upgrade openspec`.
|
||||
> On Homebrew, run `brew upgrade openspec`. On Deno, re-run the [Deno install](#deno) with `-f`; it won't overwrite the installed command without it. On Nix, use `nix profile upgrade openspec`.
|
||||
|
||||
> [!NOTE]
|
||||
> A global npm install belongs to one Node installation. Switch Node versions with nvm and the `openspec` command doesn't come along, so install it again under the new version.
|
||||
@@ -144,7 +158,7 @@ openspec completion uninstall
|
||||
npm uninstall -g @fission-ai/openspec
|
||||
```
|
||||
|
||||
On Deno: `deno uninstall --global openspec`. On Nix: `nix profile remove openspec`. Your shell should no longer find `openspec`.
|
||||
On Homebrew: `brew uninstall openspec`. On Deno: `deno uninstall --global openspec`. On Nix: `nix profile remove openspec`. Your shell should no longer find `openspec`.
|
||||
|
||||
**3. Delete what's left, or keep it.**
|
||||
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
# Quickstart
|
||||
|
||||
> Your first change on your existing repo, from idea to archived.
|
||||
> Your first change, from idea to archived, in a new or existing project.
|
||||
|
||||
Before you start, you need the CLI on your machine ([Installation](installation.md)) and OpenSpec initialized in your project ([Set up your project](setup.md)).
|
||||
|
||||
## Start from an empty project
|
||||
|
||||
You can start without a chosen stack or a complete architecture. Initialize OpenSpec in your project folder, then ask your agent to explore the options with you. In your AI chat:
|
||||
|
||||
```text
|
||||
Help me explore a task tracker from scratch. I have not picked a stack. Compare the options and help me choose the first behavior to build.
|
||||
```
|
||||
|
||||
Decide what the first change needs and leave later architecture choices open. Ask your agent to propose that one change, then follow the steps below. You can revisit the architecture as the project grows.
|
||||
|
||||
## The loop at a glance
|
||||
|
||||
Every change moves through the same five steps: you think the idea through with your agent, it drafts a plan, you correct the plan before any code exists, the agent builds from it, and archiving updates your specs with what shipped.
|
||||
@@ -17,14 +27,14 @@ flowchart LR
|
||||
archive -. "next change" .-> explore
|
||||
```
|
||||
|
||||
Every prompt below goes in your AI chat, the same place you ask for code. Each invokes an OpenSpec skill by name, the same spelling in every tool. A plain ask works too ("propose a change to add rate limiting"), and so does naming the step directly - "openspec propose", "opsx apply" - which runs the workflow instead of hand-building the files. (`openspec update` is a real CLI command that refreshes generated files, so say "openspec update change" for that workflow.) Some tools add shorter command aliases (`/opsx:propose` in Claude Code, [other tools vary](../reference/supported-tools.md)).
|
||||
Every prompt below goes in your AI chat, the same place you ask for code. The examples use plain language so they work across tools. You can also invoke a skill directly; the syntax varies by tool ([supported tools](../reference/supported-tools.md)).
|
||||
|
||||
## Step 1: Explore
|
||||
|
||||
Think the idea through with your agent before you ask for a plan. In your AI chat:
|
||||
|
||||
```text
|
||||
/openspec-explore how rate limiting should work in this app
|
||||
Help me explore how rate limiting should work in this app.
|
||||
```
|
||||
|
||||
Explore is a thinking mode. The agent investigates your codebase, asks the questions that matter, sketches options, and challenges assumptions. It never writes code. It writes nothing else unless you ask it to capture what you decided, or say yes when it offers. The output is a sharper idea.
|
||||
@@ -32,7 +42,7 @@ Explore is a thinking mode. The agent investigates your codebase, asks the quest
|
||||
Stay here as long as the problem needs. When the shape feels right, hand it off:
|
||||
|
||||
```text
|
||||
/openspec-propose
|
||||
Propose the change we just discussed.
|
||||
```
|
||||
|
||||
That line starts propose for you, carrying everything you settled. Skip the first prompt in step 2.
|
||||
@@ -42,7 +52,7 @@ That line starts propose for you, carrying everything you settled. Skip the firs
|
||||
Propose turns the idea into a reviewable plan. Coming from explore, it's already running. Starting cold, when the change is clear in your head, ask directly. In your AI chat:
|
||||
|
||||
```text
|
||||
/openspec-propose add rate limiting
|
||||
Propose a change to add rate limiting.
|
||||
```
|
||||
|
||||
The agent asks what it needs to, then writes a change folder:
|
||||
@@ -75,7 +85,7 @@ To fix something, either works:
|
||||
Apply turns the plan into code. Start a fresh chat session, since implementation goes better on a clean context window. In your AI chat:
|
||||
|
||||
```text
|
||||
/openspec-apply-change add-rate-limiting
|
||||
Apply the add-rate-limiting change.
|
||||
```
|
||||
|
||||
The agent reads the change folder, then works through `tasks.md`, checking off each task as it lands.
|
||||
@@ -91,7 +101,7 @@ Archiving does two things: it updates your main specs with the change's requirem
|
||||
When every box in `tasks.md` is checked, in your AI chat:
|
||||
|
||||
```text
|
||||
/openspec-archive-change add-rate-limiting
|
||||
Archive the add-rate-limiting change.
|
||||
```
|
||||
|
||||
Step through what archiving does:
|
||||
@@ -146,14 +156,24 @@ Step through what archiving does:
|
||||
└── 2026-08-08-add-rate-limiting/
|
||||
```
|
||||
|
||||
Git is a separate concern. Commit the change folder with the code, and nothing else about your workflow changes. When to archive relative to a PR is a team convention; the [Teams](../guides/teams.md) guide has the tradeoff.
|
||||
Git is a separate concern. Commit the change folder with the code, and nothing else about your workflow changes.
|
||||
|
||||
### Keep or prune archived changes
|
||||
|
||||
`openspec/changes/archive/` keeps the proposal, design, tasks, and delta for each finished change. In the archive flow above, the delta has already updated `openspec/specs/`.
|
||||
|
||||
- **Keep the whole change folder** when you want a self-contained record in the current checkout.
|
||||
- **Remove an archived change's `specs/` folder** when Git is your spec history. Commit the archive first, then delete `openspec/changes/archive/<change>/specs/`. The proposal, design, and tasks remain in the checkout. `openspec validate --archived` still works because it checks task completion, not applied deltas.
|
||||
- **Remove the whole change folder** only when you no longer need its proposal, design, or task history in the checkout. The current specs do not change, but `openspec validate --archived` and searches of the checkout no longer include that change.
|
||||
|
||||
> [!WARNING]
|
||||
> Keep the archive commit in your repository history if you want Git to retain the deleted files. Squashing the archive and cleanup commits together removes that intermediate snapshot.
|
||||
|
||||
OpenSpec does not prune archived changes automatically or provide a retention setting.
|
||||
|
||||
## Going further
|
||||
|
||||
- [Concepts](../guides/concepts.md): what the two artifacts are, and how a delta describes a change.
|
||||
- [Explore](../guides/explore.md): getting more out of explore mode.
|
||||
- [Apply](../guides/apply.md): pacing, context windows, resuming long changes.
|
||||
- [Review the plan](../guides/review-the-plan.md): what to look for in specs before you build.
|
||||
- [Delta specs](../reference/schemas/spec-driven/index.md#delta-specs-specmd): how to write the behavior changes in a delta spec.
|
||||
- [Profiles](../customize/profiles.md): optional workflows beyond the core set (verify before archive, incremental planning).
|
||||
|
||||
## Advanced guides
|
||||
|
||||
+40
-2
@@ -34,6 +34,22 @@ Re-running init is safe:
|
||||
- Running init again with a new tool selected adds that tool.
|
||||
- The `--tools` flag skips the picker ([CLI reference](../reference/cli.md)).
|
||||
|
||||
### Migrate an existing `project.md`
|
||||
|
||||
Init does not copy legacy `openspec/project.md` into `config.yaml`. It keeps the file and prints an AI-assisted migration request.
|
||||
|
||||
In your AI chat:
|
||||
|
||||
```
|
||||
Review openspec/project.md and migrate its useful content to openspec/config.yaml.
|
||||
Keep context concise: include only project-wide facts needed during artifact creation, apply, and archive.
|
||||
Move artifact-specific guidance into rules for the matching artifacts.
|
||||
Move guidance for apply or archive into the matching operations entry.
|
||||
Leave out generic, outdated, or verbose material. Do not delete project.md.
|
||||
```
|
||||
|
||||
Review `config.yaml`, then delete `project.md` when ready.
|
||||
|
||||
## What init installs
|
||||
|
||||
Running init creates two things in your project:
|
||||
@@ -41,7 +57,7 @@ Running init creates two things in your project:
|
||||
- An `openspec/` folder at the repo root
|
||||
- Workflow files (skills and commands) added to your AI tool's folder (`.agents/`, `.claude/`, etc.)
|
||||
|
||||
Commit all of it like the rest of your source ([FAQ](../help/faq.md) covers why). Init changes nothing else in your repo (if it finds leftovers from an older OpenSpec version, it asks before cleaning them up).
|
||||
Commit all of it like the rest of your source. Init changes nothing else in your repo (if it finds leftovers from an older OpenSpec version, it asks before cleaning them up).
|
||||
|
||||
### The `openspec/` folder
|
||||
|
||||
@@ -55,7 +71,7 @@ openspec/
|
||||
└── archive/ completed changes move here
|
||||
```
|
||||
|
||||
[Concepts](../guides/concepts.md) explains both artifacts; [Project config](../customize/project-config.md) covers `config.yaml`.
|
||||
[Project config](../customize/project-config.md) covers `config.yaml`.
|
||||
|
||||
### The workflow files (skills and commands)
|
||||
|
||||
@@ -112,4 +128,26 @@ Config changes:
|
||||
|
||||
Answering yes applies it to the current project on the spot. Other projects pick it up on their next `openspec update`. The setting is global, per machine.
|
||||
|
||||
#### Claude Code doesn't show the workflows
|
||||
|
||||
Claude Code loads OpenSpec workflows from one or both of these project paths, based on your delivery setting:
|
||||
|
||||
- **Skills**: `.claude/skills/openspec-*/SKILL.md`
|
||||
- **Commands**: `.claude/commands/opsx/<id>.md`
|
||||
|
||||
If the files are missing, refresh the project. In your terminal:
|
||||
|
||||
```bash
|
||||
openspec update
|
||||
```
|
||||
|
||||
If the command files exist but `/opsx:` shows no OpenSpec commands, update Claude Code and restart it. If commands still don't load, enable skills too. In your terminal:
|
||||
|
||||
```bash
|
||||
openspec config set delivery both
|
||||
openspec update
|
||||
```
|
||||
|
||||
Restart Claude Code, then run `/openspec-propose` in its chat. If only some workflows are missing, [change your profile](../customize/profiles.md#expanding-the-set-optional-workflows).
|
||||
|
||||
Setup is done. The [Quickstart](quickstart.md) takes your first change from here.
|
||||
|
||||
@@ -52,13 +52,13 @@ deliberately remains the compatibility bare array documented in §4.13:
|
||||
`warnings` (omitted when empty) reports directories under `changes/` that are not changes. Today the only code is `nested_change_directory`: a namespace folder wrapping change directories, which OpenSpec cannot address because a change is always a directory directly under `changes/`. The same entry carries `nested` on the listed change, whose `status` is then meaningless. Do not treat such an entry as a change; report the message and leave the directories alone.
|
||||
|
||||
### 4.2 `show <item> --json`
|
||||
Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }`.
|
||||
Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }`. A requirement, in a spec or in a change delta's `requirement`/`requirements`, is `{ "name", "text", "scenarios": [ { "name", "rawText" } ] }`. A requirement `name` is its header without `Requirement:` and without a closing `#` run, the exact name archive matches MODIFIED/REMOVED/RENAMED entries against. A scenario `name` is its level-4 header without `Scenario:` and without a closing `#` run, the name the MODIFIED scenario-loss check compares.
|
||||
|
||||
### 4.3 `validate --json`
|
||||
`{ "items": [ { "id", "type": "change"|"spec", "valid", "issues": [ { "level", "path", "message", "line"?, "column"? } ], "durationMs" } ], "summary": { "totals": {items,passed,failed}, "byType": {...} }, "version": "1.0", "root" }`. Exit 1 when any item fails.
|
||||
|
||||
### 4.4 `status --json`
|
||||
`{ "changeName", "schemaName", "planningHome"?: { "kind", "root", "changesDir", "defaultSchema" }, "changeRoot", "artifactPaths": { "<id>": {outputPath, resolvedOutputPath, existingOutputPaths} }, "nextSteps": ["..."], "actionContext": { "mode": "repo-local", "sourceOfTruth": "repo", "planningArtifacts", "linkedContext", "allowedEditRoots", "requiresAffectedAreaSelection", "constraints" }, "isPlanningComplete", "isComplete", "applyRequires", "artifacts": [ {id, outputPath, status: "done"|"skipped"|"ready"|"blocked", requires, missingDeps?} ], "root" }`. `isPlanningComplete` means every non-skipped planning artifact exists; skipped artifacts count as satisfied without being created. It does not mean implementation tasks are complete. `isComplete` is retained as a compatibility alias with the same value. Each artifact's `requires` is its direct dependency ids (present for every status, so the transitive required set is computable even when the artifact is `done`); `missingDeps` appears only when `blocked`. The `artifacts` array is in dependency order, with the schema's `artifacts:` declaration order breaking ties between artifacts that become ready at the same time (never alphabetical), so the first `ready` entry is the artifact to write next; `missingDeps` uses that same order. `"skipped"` marks an artifact whose `generates` path is under `specs/` in a change whose `.openspec.yaml` declares `skip_specs: true`; it satisfies dependencies but must not be created. No active changes: `{ "changes": [], "message", "root" }`, exit 0.
|
||||
`{ "changeName", "schemaName", "planningHome"?: { "kind", "root", "changesDir", "defaultSchema" }, "changeRoot", "artifactPaths": { "<id>": {outputPath, resolvedOutputPath, existingOutputPaths} }, "nextSteps": ["..."], "actionContext": { "mode": "repo-local", "sourceOfTruth": "repo", "planningArtifacts", "linkedContext", "allowedEditRoots", "requiresAffectedAreaSelection", "constraints" }, "isPlanningComplete", "isComplete", "applyRequires", "artifacts": [ {id, outputPath, status: "done"|"skipped"|"ready"|"blocked", requires, missingDeps?} ], "root" }`. For a store-selected root, `allowedEditRoots` is `[<declaring project>, <store>]` when the nearest project on the current path is a config-only root whose `store:` pointer names that store, and `[<store>]` otherwise (including a global `defaultStore`), with a constraint telling the agent to ask which repository to edit. OpenSpec does not route a store's tasks to repos, so the declaring project is the current one, not every repo the change touches. `isPlanningComplete` means every non-skipped planning artifact exists; skipped artifacts count as satisfied without being created. It does not mean implementation tasks are complete. `isComplete` is retained as a compatibility alias with the same value. Each artifact's `requires` is its direct dependency ids (present for every status, so the transitive required set is computable even when the artifact is `done`); `missingDeps` appears only when `blocked`. The `artifacts` array is in dependency order, with the schema's `artifacts:` declaration order breaking ties between artifacts that become ready at the same time (never alphabetical), so the first `ready` entry is the artifact to write next; `missingDeps` uses that same order. `"skipped"` marks an artifact whose `generates` path is under `specs/` in a change whose `.openspec.yaml` declares `skip_specs: true`; it satisfies dependencies but must not be created. No active changes: `{ "changes": [], "message", "root" }`, exit 0.
|
||||
|
||||
`--all` (batch, mutually exclusive with `--change` — combining them is an error with the `{ "changes": [], "root": null, "status": [d] }` null-shape): `{ "changes": [ <per-change status object, no per-change root>, ... ], "root" }`, sorted by change name. A change that fails to load contributes `{ "changeName", "status": [d] }` in place; the sweep continues, preserves the complete envelope, and exits 1 in both text and JSON modes. An invalid `--schema` fails the whole invocation with the null-shape, even when no changes exist.
|
||||
|
||||
@@ -79,6 +79,10 @@ Success: `{ "change": { "id", "path", "metadataPath", "schema" }, "root" }`. Fai
|
||||
### 4.9 `archive <name> --json`
|
||||
Success: `{ "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"?, "warnings"? }, "root" }`. Failure: `{ "archive": null, "root"?, "status": [d] }`, exit 1. `specsUpdated` is true only when at least one spec file was written or retired (a capability whose last requirement the change removed has its spec deleted, which requires `retire_capabilities: true` in the change's `.openspec.yaml`; every retirement is named in `warnings`, with a pasteable Git recovery command only when the spec lived in the caller's checkout); an already-synced change archives with all-zero totals and the skips listed in `warnings`. JSON mode is strictly non-interactive: every prompt point becomes an `archive_*` code.
|
||||
|
||||
- **`archive: null`**: the command failed. This is not a guarantee that files are unchanged.
|
||||
- **`archive_retirement_cleanup_failed`**: the change was archived, but retirement backup verification or cleanup failed. A listed backup path may have changed or disappeared. The message can also report a staged source left by a failed fallback-copy cleanup. Inspect the current archive and all reported recovery paths before cleanup. Preserve any needed content. Retrying archive does not clean up these paths.
|
||||
- **`archive_error`**: the fallback diagnostic does not identify whether files changed. Inspect the change, archive, and affected specs before retrying.
|
||||
|
||||
### 4.10 `doctor --json`
|
||||
`{ "root": { "path", "source", "store_id"?, "healthy", "status": [] }, "store": { "id", "metadata": {present,valid,remote?}, "origin_url"?, "drift"?: {ahead,behind}, "status": [] } | null, "references": [...], "status": [] }`. `drift` (present only for a git-backed store checkout that has an upstream tracking ref) is ahead/behind counts against the last-fetched upstream, not the live remote. Health findings of any severity exit 0. Failure payload: `{ "root": null, "store": null, "references": [], "status": [d] }`, exit 1.
|
||||
|
||||
@@ -124,7 +128,7 @@ setup/register: `{ "store": {id, root, metadata_path?}, "registry": {path, regis
|
||||
`relationship_registry_unreadable`, `root_pointer_ignored`, `root_pointer_invalid`, `pointer_declarations_inert`.
|
||||
|
||||
### Archive (JSON mode)
|
||||
`archive_change_name_required`, `archive_change_not_found`, `archive_change_symlink`, `archive_validation_failed`, `archive_confirmation_required`, `archive_tasks_incomplete`, `archive_spec_update_failed`, `archive_spec_validation_failed`, `archive_target_exists`, `archive_error`.
|
||||
`archive_change_name_required`, `archive_change_not_found`, `archive_change_symlink`, `archive_validation_failed`, `archive_confirmation_required`, `archive_tasks_incomplete`, `archive_spec_update_failed`, `archive_spec_validation_failed`, `archive_target_exists`, `archive_retirement_cleanup_failed`, `archive_error`.
|
||||
|
||||
### Context writes
|
||||
`context_file_exists`, `context_output_dir_missing`.
|
||||
|
||||
+8
-2
@@ -352,7 +352,13 @@ Revise a change's existing planning artifacts and keep them coherent with one an
|
||||
- Applies your requested revision, or reviews the artifacts for contradictions if you didn't name one
|
||||
- Reconciles the other existing artifacts in any direction (a design edit may ripple back to the proposal)
|
||||
- Confirms every edit with you before writing, one artifact at a time
|
||||
- Ends by recommending the next step: `/opsx:continue` (artifacts missing), `/opsx:apply` (carry a revised plan into code), or `/opsx:archive` (all done)
|
||||
- Ends by recommending the next step: `/opsx:continue` (unstarted artifacts), `/opsx:apply` (carry a revised plan into code), or `/opsx:archive` (all done)
|
||||
|
||||
**Missing files:**
|
||||
|
||||
- For a glob artifact such as `specs/**/*.md` with at least one existing file, update can propose a missing companion file. It uses the schema's instructions and asks you to confirm the concrete path before creating it.
|
||||
- Artifacts with no files yet remain with `/opsx:continue`. Intentionally skipped artifacts stay untouched.
|
||||
- New files must stay inside the change directory. If a file appears at the confirmed path before creation, update stops instead of overwriting it.
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -373,7 +379,7 @@ AI: Reading add-dark-mode artifacts...
|
||||
|
||||
**Tips:**
|
||||
|
||||
- It won't create missing artifacts - that's `/opsx:continue`
|
||||
- It won't start an artifact with no existing files. Enable `/opsx:continue` for that, or use `openspec status` and `openspec instructions` if that optional workflow isn't installed.
|
||||
- If the change was already implemented, follow up with `/opsx:apply` so the code matches the revised plan
|
||||
- If your revision changes the *intent* of the change, start fresh with a new change instead (see [When to Update vs. Start Fresh](opsx.md#when-to-update-vs-start-fresh))
|
||||
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@ Listed projects are maintained independently. Inclusion does not imply official
|
||||
|
||||
## Projects and resources
|
||||
|
||||
- **[OpenSpec UI](https://github.com/VeryComplexAndLongName/OpenSpec-UI)**: A standalone web dashboard and VS Code extension for browsing OpenSpec changes, archives, specs, and tasks.
|
||||
- **[OpenSpec Workbench](https://github.com/VeryComplexAndLongName/OpenSpec-UI)**: Running and supervising agents on OpenSpec changes.
|
||||
- **[MySpec](https://myspec.dev)**: Cloud beta (account required) that conducts guided spec interviews and exports four-file bundles or OpenSpec-compatible changes. The service sends submitted content to third-party AI providers and is not designed for sensitive data.
|
||||
- **[openspec-guard](https://github.com/guillaume-flambard/spec-guard)**: CLI and GitHub Action that reports which OpenSpec scenarios are covered by a Vitest or Jest test, without running the tests.
|
||||
|
||||
## Add your project
|
||||
|
||||
|
||||
@@ -341,6 +341,8 @@ Tasks are the **implementation checklist** — concrete steps with checkboxes.
|
||||
- Group related tasks under headings
|
||||
- Use hierarchical numbering (1.1, 1.2, etc.)
|
||||
- Keep tasks small enough to complete in one session
|
||||
- State how each task is verified (a test, command, or observable result)
|
||||
- Land the tests and documentation each group's work calls for inside that group, not in a final catch-up group
|
||||
- Check tasks off as you complete them
|
||||
|
||||
## Delta Specs
|
||||
|
||||
+3
-1
@@ -213,7 +213,9 @@ Works through tasks, checking them off as you go. If you're juggling multiple ch
|
||||
```
|
||||
/opsx:update add-dark-mode - we're storing the theme in a cookie now
|
||||
```
|
||||
Revises the change's existing planning artifacts and keeps them coherent - in any direction (a design edit may ripple back to the proposal). Planning artifacts only: it never edits code, and it never creates missing artifacts (that's `/opsx:continue`). Every edit is confirmed with you first. If the change was already implemented, it recommends `/opsx:apply` so the code catches up with the revised plan. If your revision changes the change's *intent*, start fresh instead - see [When to Update vs. Start Fresh](#when-to-update-vs-start-fresh).
|
||||
Revises the change's existing planning artifacts and keeps them coherent in any direction (a design edit may ripple back to the proposal). It never edits code. Every edit is confirmed with you first. See [the update reference](commands.md#opsxupdate) for how it handles missing files without starting a new artifact.
|
||||
|
||||
If the change was already implemented, it recommends `/opsx:apply` so the code catches up with the revised plan. If your revision changes the change's *intent*, start fresh instead. See [When to Update vs. Start Fresh](#when-to-update-vs-start-fresh).
|
||||
|
||||
### Sync delta specs
|
||||
```text
|
||||
|
||||
@@ -86,7 +86,7 @@ to read the hint.
|
||||
| Hermes Agent (`hermes`) | `.hermes/skills/openspec-*/SKILL.md`\*\*\* | Not generated (no command adapter; use skill-based `/openspec-*` invocations) |
|
||||
| iFlow (`iflow`) | `.iflow/skills/openspec-*/SKILL.md` | `.iflow/commands/opsx-<id>.md` |
|
||||
| Junie (`junie`) | `.junie/skills/openspec-*/SKILL.md` | `.junie/commands/opsx-<id>.md` |
|
||||
| Kilo Code (`kilocode`) | `.kilocode/skills/openspec-*/SKILL.md` | `.kilocode/workflows/opsx-<id>.md` |
|
||||
| Kilo Code (`kilocode`) | `.kilocode/skills/openspec-*/SKILL.md` | `.kilo/command/opsx-<id>.md` |
|
||||
| Kimi Code (`kimi`) | `.kimi-code/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/skill:openspec-*` invocations) |
|
||||
| Kiro (`kiro`) | `.kiro/skills/openspec-*/SKILL.md` | `.kiro/prompts/opsx-<id>.prompt.md` |
|
||||
| Lingma (`lingma`) | `.lingma/skills/openspec-*/SKILL.md` | `.lingma/commands/opsx/<id>.md` |
|
||||
|
||||
@@ -15,87 +15,98 @@
|
||||
"aarch64-darwin"
|
||||
];
|
||||
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems f;
|
||||
|
||||
pkgsFor =
|
||||
system:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ self.overlays.default ];
|
||||
};
|
||||
in
|
||||
{
|
||||
overlays.default = final: _prev: {
|
||||
openspec = final.stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "openspec";
|
||||
version = (builtins.fromJSON (builtins.readFile ./package.json)).version;
|
||||
|
||||
src = final.lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = final.lib.fileset.unions [
|
||||
./src
|
||||
./bin
|
||||
./schemas
|
||||
./scripts
|
||||
./test
|
||||
./package.json
|
||||
./pnpm-lock.yaml
|
||||
./pnpm-workspace.yaml
|
||||
./tsconfig.json
|
||||
./build.js
|
||||
./vitest.config.ts
|
||||
./vitest.setup.ts
|
||||
./eslint.config.js
|
||||
];
|
||||
};
|
||||
|
||||
pnpmDeps = final.fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = final.pnpm_10;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-gPGdwmj4oLb/j3D/BGNCaI0hFfrHKCjH4I71UYzmhfk=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with final; [
|
||||
installShellFiles
|
||||
nodejs_22
|
||||
npmHooks.npmInstallHook
|
||||
pnpmConfigHook
|
||||
pnpm_10
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
pnpm run build
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
dontNpmPrune = true;
|
||||
|
||||
# `openspec completion generate` renders a static command registry, so it
|
||||
# needs no project and no network. Opting out of telemetry also disables
|
||||
# the update check, keeping the build offline.
|
||||
postInstall = final.lib.optionalString (final.stdenv.buildPlatform.canExecute final.stdenv.hostPlatform) ''
|
||||
export OPENSPEC_TELEMETRY=0
|
||||
completions=$(mktemp -d)
|
||||
for shell in bash fish zsh; do
|
||||
$out/bin/openspec completion generate "$shell" > "$completions/openspec.$shell"
|
||||
done
|
||||
installShellCompletion --cmd openspec \
|
||||
--bash "$completions/openspec.bash" \
|
||||
--fish "$completions/openspec.fish" \
|
||||
--zsh "$completions/openspec.zsh"
|
||||
'';
|
||||
|
||||
meta = with final.lib; {
|
||||
description = "AI-native system for spec-driven development";
|
||||
homepage = "https://github.com/Fission-AI/OpenSpec";
|
||||
license = licenses.mit;
|
||||
maintainers = [ ];
|
||||
mainProgram = "openspec";
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
packages = forAllSystems (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
inherit (pkgs) lib;
|
||||
pkgs = pkgsFor system;
|
||||
in
|
||||
{
|
||||
default = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "openspec";
|
||||
version = (builtins.fromJSON (builtins.readFile ./package.json)).version;
|
||||
|
||||
src = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.unions [
|
||||
./src
|
||||
./bin
|
||||
./schemas
|
||||
./scripts
|
||||
./test
|
||||
./package.json
|
||||
./pnpm-lock.yaml
|
||||
./pnpm-workspace.yaml
|
||||
./tsconfig.json
|
||||
./build.js
|
||||
./vitest.config.ts
|
||||
./vitest.setup.ts
|
||||
./eslint.config.js
|
||||
];
|
||||
};
|
||||
|
||||
pnpmDeps = pkgs.fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = pkgs.pnpm_10;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-oz4tsfu05IPDMaBBp5jLbfsxvTmw1oVtNFtpvudCOPE=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
installShellFiles
|
||||
nodejs_22
|
||||
npmHooks.npmInstallHook
|
||||
pnpmConfigHook
|
||||
pnpm_10
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
pnpm run build
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
dontNpmPrune = true;
|
||||
|
||||
# `openspec completion generate` renders a static command registry, so it
|
||||
# needs no project and no network. Opting out of telemetry also disables
|
||||
# the update check, keeping the build offline.
|
||||
postInstall = lib.optionalString (pkgs.stdenv.buildPlatform.canExecute pkgs.stdenv.hostPlatform) ''
|
||||
export OPENSPEC_TELEMETRY=0
|
||||
completions=$(mktemp -d)
|
||||
for shell in bash fish zsh; do
|
||||
$out/bin/openspec completion generate "$shell" > "$completions/openspec.$shell"
|
||||
done
|
||||
installShellCompletion --cmd openspec \
|
||||
--bash "$completions/openspec.bash" \
|
||||
--fish "$completions/openspec.fish" \
|
||||
--zsh "$completions/openspec.zsh"
|
||||
'';
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "AI-native system for spec-driven development";
|
||||
homepage = "https://github.com/Fission-AI/OpenSpec";
|
||||
license = licenses.mit;
|
||||
maintainers = [ ];
|
||||
mainProgram = "openspec";
|
||||
};
|
||||
});
|
||||
default = pkgs.openspec;
|
||||
inherit (pkgs) openspec;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2025-12-29
|
||||
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
Amp reads project skills from `.agents/skills/`, but OpenSpec does not list Amp in its tool picker or accept `amp` through `--tools`. Amp users can select the universal `.agents` target, but only if they already know how Amp discovers skills.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add Amp as a supported skills-only tool with `amp` as its tool id.
|
||||
- Generate Amp's OpenSpec skills through the existing shared `.agents/skills/` pipeline.
|
||||
- Detect Amp projects from `.amp/` and recognize Amp-owned OpenSpec skill trees during update.
|
||||
- Document Amp's paths and invocation syntax in the docs-lab supported-tools reference.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_None._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ai-tool-paths`: define Amp's shared Agent Skills path and skills-only behavior.
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/config.ts`: add the Amp tool metadata.
|
||||
- `test/core/init.test.ts`, `test/core/update.test.ts`, and `test/core/available-tools.test.ts`: cover generation, refresh, and detection.
|
||||
- `docs-lab/reference/supported-tools.md`: add Amp to the support matrix and shared-folder notes.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Adding an Amp command adapter. Amp's supported project extension surface is Agent Skills.
|
||||
- Adding a second Amp-specific skill generator or template set.
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Amp skills integration
|
||||
|
||||
OpenSpec SHALL expose Amp as a supported skills-only tool that uses Amp's project Agent Skills directory.
|
||||
|
||||
#### Scenario: Selecting Amp
|
||||
|
||||
- **WHEN** the user selects Amp in `openspec init` or passes `--tools amp`
|
||||
- **THEN** OpenSpec SHALL generate the active profile's skills under `.agents/skills/`
|
||||
- **AND** the generated skills SHALL use `/openspec-<skill>` references
|
||||
- **AND** OpenSpec SHALL NOT generate command files for Amp
|
||||
|
||||
#### Scenario: Detecting an Amp project
|
||||
|
||||
- **WHEN** a project contains an `.amp/` directory
|
||||
- **THEN** OpenSpec SHALL detect Amp as an available tool
|
||||
|
||||
#### Scenario: Updating an Amp-owned skill tree
|
||||
|
||||
- **GIVEN** `.agents/skills/.openspec-target` names `amp`
|
||||
- **WHEN** `openspec update` runs
|
||||
- **THEN** OpenSpec SHALL refresh the Amp skill tree through the shared skill generator
|
||||
|
||||
#### Scenario: Sharing the Agent Skills directory
|
||||
|
||||
- **WHEN** Amp is selected with another tool that writes `.agents/skills/`
|
||||
- **THEN** OpenSpec SHALL write one compatible skill tree rather than letting the tools overwrite each other
|
||||
@@ -0,0 +1,20 @@
|
||||
## 1. Tool support
|
||||
|
||||
- [x] 1.1 Add Amp to `AI_TOOLS` as a skills-only `.agents` target.
|
||||
- [x] 1.2 Detect Amp projects from `.amp/` and preserve shared-root ownership.
|
||||
|
||||
## 2. Documentation
|
||||
|
||||
- [x] 2.1 Add Amp to the docs-lab supported-tools matrix.
|
||||
- [x] 2.2 Document Amp's skills-only and shared-folder behavior.
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 Cover Amp detection from `.amp/` and `.openspec-target`.
|
||||
- [x] 3.2 Cover init generation, Agent Skills frontmatter, invocation syntax, and command skipping.
|
||||
- [x] 3.3 Cover update of an Amp-owned shared skill tree.
|
||||
|
||||
## 4. Verification
|
||||
|
||||
- [x] 4.1 Validate `add-amp-support` in strict mode.
|
||||
- [x] 4.2 Run targeted tests, lint, build, and the full test suite.
|
||||
@@ -105,6 +105,12 @@ Review feedback flagged that "update" alone is generic — could it apply to any
|
||||
### 6. Next-step guidance, especially for already-implemented changes
|
||||
A change can be revised after it was built — tasks checked off, `/opsx:apply` already run. The update itself behaves identically (planning artifacts only), but stopping silently would strand the user: the code and the revised plan now disagree. So the skill ends by reporting where the change stands (from the status JSON and the tasks checklist) and recommending the next command — `/opsx:continue` if artifacts are missing, `/opsx:apply` to carry a revised plan into code, `/opsx:archive` when everything is done. Guidance only: the skill never implements, mirroring the "All artifacts created! You can now implement this change with `/opsx:apply`" hand-off that `continue-change.ts` already uses.
|
||||
|
||||
### 7. Companion-file correction (#1733)
|
||||
|
||||
The original glob-file deferral was unreachable: one matching file marks an artifact `done`, while continue selects only `ready` artifacts. Update can therefore propose a missing companion file within an already populated glob. This corrects the unarchived spec's former blanket deferral without changing the graph's completion rule or starting another artifact.
|
||||
|
||||
The exception uses existing status and instructions output, requires current dependency context and user confirmation, and preserves the change-only planning scope. Immediately before creation, it rechecks scope and the concrete path and uses an operation that refuses an existing target. Delegated creators must obey the same limits. No new CLI command, metadata, graph state, or automatic artifact writer is introduced.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **No deterministic staleness signal.** With no digest/ledger, the skill relies on the agent reading the artifacts to spot incoherence. Trade-off accepted: an agent that rewrites prose must read it anyway, and a content-blind signal earns its cost only for use cases this change excludes (Decision 3).
|
||||
|
||||
@@ -19,7 +19,7 @@ The system SHALL provide a `/opsx:update` workflow skill that revises a change's
|
||||
|
||||
#### Scenario: Missing artifacts are deferred to continue
|
||||
|
||||
- **WHEN** keeping the change coherent would require an artifact that has not been created yet
|
||||
- **WHEN** keeping the change coherent would require an artifact with no existing output files and status `ready` or `blocked`
|
||||
- **THEN** the skill revises only the artifacts that currently exist
|
||||
- **AND** it notes the not-yet-created artifacts and points the user to `/opsx:continue` to create them
|
||||
|
||||
@@ -53,7 +53,7 @@ The `/opsx:update` skill SHALL learn which artifacts exist and where they live b
|
||||
|
||||
#### Scenario: Resolve artifact paths cross-platform
|
||||
|
||||
- **WHEN** the skill reads or writes an artifact on macOS, Linux, or Windows
|
||||
- **WHEN** the skill reads or revises an existing artifact file on macOS, Linux, or Windows
|
||||
- **THEN** it uses the `existingOutputPaths` provided by the CLI status output
|
||||
- **AND** it does not assume forward-slash separators
|
||||
|
||||
@@ -63,11 +63,30 @@ The `/opsx:update` skill SHALL learn which artifacts exist and where they live b
|
||||
- **THEN** the skill edits the concrete files reported in that artifact's `existingOutputPaths`
|
||||
- **AND** it does not write to `resolvedOutputPath`, which for a glob artifact remains the glob pattern rather than a real file
|
||||
|
||||
#### Scenario: A new file under a glob artifact is deferred to continue
|
||||
#### Scenario: A missing companion file under a populated glob artifact
|
||||
|
||||
- **WHEN** keeping the change coherent would require a new file under a glob artifact that does not exist yet (for example a spec for a not-yet-captured capability)
|
||||
- **THEN** the skill revises only the files already present in `existingOutputPaths`
|
||||
- **AND** it points the user to `/opsx:continue`/`/opsx:propose` to create the new file rather than inventing a path from the glob
|
||||
- **WHEN** reconciliation identifies a missing companion file for a glob artifact with non-empty `existingOutputPaths`
|
||||
- **THEN** the skill MAY propose creating that file using the artifact's instructions, template, project context, rules, and current dependency files
|
||||
- **AND** it selects an unused concrete path matching the artifact's `outputPath` inside `changeRoot`, including after resolving linked parent directories
|
||||
- **AND** it creates the file only after user confirmation, refreshing status, instructions, and path checks immediately before creation
|
||||
- **AND** creation SHALL fail rather than overwrite a file that appeared in the meantime
|
||||
- **AND** it SHALL NOT start another artifact, write main specs, or edit implementation code
|
||||
|
||||
#### Scenario: Required inputs are no longer available
|
||||
|
||||
- **WHEN** a populated glob artifact remains `done` but a required non-skipped dependency is missing
|
||||
- **THEN** the skill SHALL stop new companion creation and ask the user to restore the dependency first
|
||||
|
||||
#### Scenario: Schema delegates companion creation
|
||||
|
||||
- **WHEN** the artifact instruction delegates creation to another skill or command
|
||||
- **THEN** the skill SHALL invoke it only if it can honor the confirmed concrete path and the update guardrails
|
||||
- **AND** otherwise it SHALL stop rather than invoke broader generation
|
||||
|
||||
#### Scenario: Intentionally skipped artifact
|
||||
|
||||
- **WHEN** status or instructions mark an artifact as skipped
|
||||
- **THEN** the skill SHALL leave it untouched and SHALL NOT treat its empty outputs as missing or send it to continue
|
||||
|
||||
### Requirement: Bidirectional Coherence Review
|
||||
|
||||
@@ -109,7 +128,7 @@ After applying confirmed revisions (or finding none needed), the `/opsx:update`
|
||||
|
||||
#### Scenario: Next step when artifacts are incomplete
|
||||
|
||||
- **WHEN** the update finishes and the change still has not-yet-created artifacts
|
||||
- **WHEN** the update finishes and the change still has artifacts with no outputs and status `ready` or `blocked`
|
||||
- **THEN** the skill recommends `/opsx:continue` to create them
|
||||
|
||||
#### Scenario: Next step when the change is fully done
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-28
|
||||
@@ -0,0 +1,131 @@
|
||||
# Design: a read-only `openspec version` command
|
||||
|
||||
## Context
|
||||
|
||||
`openspec --version` is Commander's built-in version flag and intentionally prints only the package version. `src/core/version-check.ts` already knows how to locate the running package, classify several install layouts, select package-manager-specific update advice, enforce update-check privacy controls, and query a registry defensively. Those helpers currently serve `openspec update`, where a nullable return value is enough: either announce a newer release or continue silently.
|
||||
|
||||
The new command has a different contract. It must explain why no update was reported, and external tools need a stable JSON shape rather than terminal prose. That requires an additive command and a structured result from the existing version-check core; it does not require a second detection or networking implementation.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
|
||||
- Give people and tools one supported way to inspect the running version and install context.
|
||||
- Keep the default command local and instant; network access requires `--check`.
|
||||
- Return one stable JSON document suitable for editor extensions, GUIs, and scripts.
|
||||
- Reuse the existing install-detection and registry-safety rules.
|
||||
- Preserve `openspec --version` byte-for-byte for existing scripts.
|
||||
|
||||
**Non-Goals**
|
||||
|
||||
- Installing or upgrading OpenSpec; that work is tracked separately in #1989.
|
||||
- Release notes, channels, prerelease selection, or background checks.
|
||||
- Perfectly identifying every custom package-manager layout. Unknown values remain honest `null`s.
|
||||
- Changing `openspec update` or its interactive upgrade offer.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Add a command; do not extend `--version`
|
||||
|
||||
`openspec --version` is a widely scripted, root-level flag whose bare output is useful precisely because it has no other fields. Commander also treats root flags differently from subcommands. A separate `openspec version` command creates room for options and structured output without changing the old contract.
|
||||
|
||||
### 2. Separate local inspection from the network check
|
||||
|
||||
`openspec version` and `openspec version --json` inspect only local process and package paths. `--check` is the sole trigger for registry access. This makes the default deterministic, fast, and safe in offline or air-gapped environments.
|
||||
|
||||
The command remains successful when checking is disabled or unavailable. Update availability is advisory, so disabled privacy settings and network failure are data states rather than command failures.
|
||||
|
||||
### 3. Use one versioned JSON envelope
|
||||
|
||||
The JSON response always starts with the same base fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"version": "1.13.2",
|
||||
"install": {
|
||||
"location": "/path/to/@fission-ai/openspec",
|
||||
"packageManager": "npm",
|
||||
"scope": "global"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With `--check`, the response adds:
|
||||
|
||||
```json
|
||||
{
|
||||
"update": {
|
||||
"status": "available",
|
||||
"latest": "1.14.0",
|
||||
"command": "npm install -g @fission-ai/openspec@latest",
|
||||
"canSelfUpgrade": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`schemaVersion` versions the document independently of the OpenSpec package. The `update` object is absent unless requested, so local callers do not need to distinguish "not checked" from a check outcome. Nullable fields are explicit when detection has no defensible answer.
|
||||
|
||||
Status values are deliberately small:
|
||||
|
||||
- `available`: a safe newer registry version was found.
|
||||
- `current`: the check completed and found no newer version.
|
||||
- `disabled`: policy prevented a request, including privacy opt-outs or a rejected registry.
|
||||
- `offline`: a permitted request did not produce a usable answer, including timeouts and invalid responses.
|
||||
|
||||
### 4. Classify ownership before naming a package manager
|
||||
|
||||
Install scope is determined before package-manager ownership:
|
||||
|
||||
1. A source checkout reports `scope: "source"` and `packageManager: null`.
|
||||
2. An ephemeral runner/cache reports `scope: "temporary"`.
|
||||
3. A dependency owned by the current project reports `scope: "project"`.
|
||||
4. A recognized global layout reports `scope: "global"`.
|
||||
5. If no classification is defensible, the field is `null` rather than guessing.
|
||||
|
||||
The implementation should reuse `getInstallDir()`, `isSourceCheckout()`, `isEphemeralRunnerInstall()`, `isProjectLocalInstall()`, and `detectPackageManager()`, while adding one pure function that assembles the public install record. Detection stays separately unit-testable with POSIX and Windows paths.
|
||||
|
||||
### 5. Return a structured check result from the existing core
|
||||
|
||||
`getAvailableCliUpdate()` currently collapses four conditions into `null`: current, disabled, unreachable, and invalid response. Keep it as a compatibility wrapper for `openspec update`, but implement it over a new structured check function whose result maps directly to the four public statuses.
|
||||
|
||||
The structured function must share the current request implementation. It must not duplicate registry selection, TLS-only configured-registry behavior, redirect limits, timeouts, response-size limits, or safe-version validation.
|
||||
|
||||
### 6. Derive update guidance from existing decisions
|
||||
|
||||
The reported command and `canSelfUpgrade` value come from the same install classification used by `openspec update`. Refactor terminal-line builders only as needed to expose a pure structured recommendation; do not parse human-readable strings back into JSON.
|
||||
|
||||
`canSelfUpgrade` describes whether the existing safe self-upgrade mechanism could operate on this install. The version command never invokes that mechanism.
|
||||
|
||||
### 7. Keep incidental output away from JSON
|
||||
|
||||
The CLI already defers telemetry and completion notices for JSON runs. The command follows existing JSON error/output conventions and writes exactly one JSON document to stdout. Human-readable output may use multiple lines but remains uncolored when global color is disabled.
|
||||
|
||||
## Security and Privacy
|
||||
|
||||
- No network access occurs without `--check`.
|
||||
- Existing privacy opt-outs continue to block the request.
|
||||
- A rejected configured registry does not cause a fallback request to public npm.
|
||||
- Registry-provided versions pass the current strict validator before display.
|
||||
- Existing redirect, timeout, and response-size limits remain in force.
|
||||
- Install paths are printed only in direct response to the user's command and are never sent as telemetry by this change.
|
||||
- The command never executes the reported update command.
|
||||
|
||||
## Documentation
|
||||
|
||||
The implementation updates `docs-lab/reference/cli.md`, using the current docs-lab page structure and examples. It does not update the legacy `docs/cli.md` page.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Public schema commitment:** integrations may depend on field names and status values. `schemaVersion` and regression fixtures make future incompatible changes explicit.
|
||||
- **Install detection is heuristic:** custom layouts may remain unknown. Returning `null` is less convenient but safer than incorrect update guidance.
|
||||
- **Absolute path disclosure:** `install.location` can contain a user name. It appears only on explicit local invocation; callers that persist or transmit it are responsible for handling it as local environment data.
|
||||
- **Status vocabulary:** `offline` also covers unusable registry responses, not only literal network loss. It is intentionally user-facing shorthand for "no usable remote answer" while logs/tests retain the detailed cause internally if needed.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
This change is additive. Existing flags and commands retain their behavior. No stored data, configuration, or generated files require migration.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None required for implementation. Review may rename a JSON field or status before approval; after release, incompatible changes require a new `schemaVersion`.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Editor extensions, GUIs, and scripts can read OpenSpec's bare version number, but they cannot ask how this copy was installed or whether an update is available. They must duplicate OpenSpec's install detection and update-check behavior, which produces inconsistent advice and makes integrations depend on human-oriented terminal output.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add `openspec version` as a read-only command that reports the installed version and install context without requiring an OpenSpec project.
|
||||
- Add `openspec version --json` with a versioned, machine-readable response for integrations.
|
||||
- Add an opt-in `--check` flag that queries the configured registry and reports whether an update is available, disabled, current, or temporarily unavailable.
|
||||
- Reuse the existing privacy opt-outs, registry safeguards, package-manager detection, and update-command selection.
|
||||
- Keep the existing `openspec --version` output and behavior unchanged for backward compatibility.
|
||||
- Document the command in `docs-lab/reference/cli.md`; the legacy `docs/` tree is not updated.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `cli-version`: Report the installed OpenSpec version and install context, with an optional privacy-aware update check and stable JSON output.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
None.
|
||||
|
||||
## Impact
|
||||
|
||||
- Public CLI: one additive `version` command with `--json` and `--check` options.
|
||||
- Public machine interface: a new JSON document identified by `schemaVersion: 1`.
|
||||
- Version-check core: separate update-check outcomes from the current nullable result so callers can distinguish disabled, current, and unavailable states.
|
||||
- Tests: unit coverage for install classification and update outcomes, plus CLI end-to-end coverage for text/JSON output and backward compatibility.
|
||||
- Documentation: `docs-lab/reference/cli.md` only, following the current docs-lab format.
|
||||
- No new dependency, background network request, telemetry field, or automatic upgrade behavior.
|
||||
|
||||
Tracks [#1988](https://github.com/Fission-AI/OpenSpec/issues/1988); the issue remains open until implementation lands.
|
||||
@@ -0,0 +1,120 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Report the installed version
|
||||
|
||||
The system SHALL provide an `openspec version` command that reports the running OpenSpec version and install context without requiring an OpenSpec project or contacting the network.
|
||||
|
||||
#### Scenario: Human-readable version report
|
||||
|
||||
- **WHEN** a user runs `openspec version`
|
||||
- **THEN** the command reports the running OpenSpec version
|
||||
- **AND** identifies the install's package manager and scope when they can be determined
|
||||
- **AND** exits successfully without contacting a registry
|
||||
|
||||
#### Scenario: Machine-readable version report
|
||||
|
||||
- **WHEN** a user runs `openspec version --json`
|
||||
- **THEN** stdout contains one valid JSON document
|
||||
- **AND** the document contains `schemaVersion: 1`, `version`, and `install`
|
||||
- **AND** `install` contains `location`, `packageManager`, and `scope`
|
||||
- **AND** `scope` is one of `global`, `project`, `temporary`, or `source`
|
||||
- **AND** values that cannot be determined are represented as `null`
|
||||
|
||||
#### Scenario: Source checkout
|
||||
|
||||
- **WHEN** the running CLI is a source checkout
|
||||
- **THEN** the command reports `scope` as `source`
|
||||
- **AND** it does not claim that a package manager owns the checkout
|
||||
|
||||
#### Scenario: Existing version flag remains compatible
|
||||
|
||||
- **WHEN** a user runs `openspec --version`
|
||||
- **THEN** the command prints the same bare version string as before this capability was added
|
||||
- **AND** no JSON or install metadata is added to that output
|
||||
|
||||
### Requirement: Check for an available update on request
|
||||
|
||||
The system SHALL contact the configured package registry only when `openspec version` receives `--check`, and SHALL report the outcome without making command success depend on registry availability.
|
||||
|
||||
#### Scenario: Update is available
|
||||
|
||||
- **WHEN** a user runs `openspec version --check`
|
||||
- **AND** the registry reports a safe newer version
|
||||
- **THEN** the command reports the latest version
|
||||
- **AND** reports the appropriate update command only when one exists for this install
|
||||
- **AND** human-readable output omits package-manager guidance when no such command exists
|
||||
- **AND** reports whether the existing self-upgrade path can safely update this copy
|
||||
- **AND** exits successfully
|
||||
|
||||
#### Scenario: Installed version is current
|
||||
|
||||
- **WHEN** a user runs `openspec version --check`
|
||||
- **AND** the registry reports no version newer than the running version
|
||||
- **THEN** the command reports the update status as `current`
|
||||
- **AND** exits successfully
|
||||
|
||||
#### Scenario: Update check is disabled
|
||||
|
||||
- **WHEN** a user runs `openspec version --check`
|
||||
- **AND** an existing OpenSpec privacy or update-check opt-out disables registry access
|
||||
- **THEN** the command does not contact the registry
|
||||
- **AND** reports the update status as `disabled`
|
||||
- **AND** reports no latest version
|
||||
- **AND** exits successfully
|
||||
|
||||
#### Scenario: Registry is unavailable
|
||||
|
||||
- **WHEN** a user runs `openspec version --check`
|
||||
- **AND** the registry cannot be reached or returns an unusable response
|
||||
- **THEN** the command reports the update status as `offline`
|
||||
- **AND** reports no latest version
|
||||
- **AND** exits successfully
|
||||
|
||||
#### Scenario: Machine-readable update result
|
||||
|
||||
- **WHEN** a user runs `openspec version --check --json`
|
||||
- **THEN** the base version and install fields remain present
|
||||
- **AND** the document contains an `update` object with `status`, `latest`, `command`, and `canSelfUpgrade`
|
||||
- **AND** `status` is one of `available`, `current`, `disabled`, or `offline`
|
||||
- **AND** unavailable values are represented as `null`
|
||||
- **AND** stdout contains no text outside the JSON document
|
||||
|
||||
### Requirement: Preserve update-check safeguards
|
||||
|
||||
The version command SHALL use the same privacy, registry, timeout, response-size, redirect, and version-validation safeguards as OpenSpec's existing update check.
|
||||
|
||||
#### Scenario: Explicit privacy opt-out
|
||||
|
||||
- **WHEN** telemetry is disabled or `DO_NOT_TRACK`, `OPENSPEC_TELEMETRY`, or `OPENSPEC_NO_UPDATE_CHECK` disables outbound checks
|
||||
- **AND** a user runs `openspec version --check`
|
||||
- **THEN** the command performs no update-check request
|
||||
- **AND** reports the update status as `disabled`
|
||||
|
||||
#### Scenario: Unsafe configured registry
|
||||
|
||||
- **WHEN** the configured registry is rejected by the existing registry safeguards
|
||||
- **AND** a user runs `openspec version --check`
|
||||
- **THEN** the command does not fall back to the public registry
|
||||
- **AND** reports the update status as `disabled`
|
||||
|
||||
#### Scenario: Untrusted version response
|
||||
|
||||
- **WHEN** the registry response does not contain a version accepted by OpenSpec's existing version validator
|
||||
- **THEN** the command does not print the untrusted value
|
||||
- **AND** reports the update status as `offline`
|
||||
|
||||
### Requirement: Version reporting is read-only
|
||||
|
||||
The version command SHALL NOT install, upgrade, or modify OpenSpec, project files, or user configuration.
|
||||
|
||||
#### Scenario: Update is available
|
||||
|
||||
- **WHEN** `openspec version --check` reports an available update
|
||||
- **THEN** it reports guidance only
|
||||
- **AND** does not run a package manager or alter the installed copy
|
||||
|
||||
#### Scenario: Upgrade option is rejected
|
||||
|
||||
- **WHEN** a user runs `openspec version --upgrade`
|
||||
- **THEN** the command reports that `--upgrade` is not supported
|
||||
- **AND** does not attempt an upgrade
|
||||
@@ -0,0 +1,35 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Model version and install information
|
||||
|
||||
- [x] 1.1 Add typed, pure install classification that reports location, package manager, and scope without guessing ownership for source or unknown layouts.
|
||||
- [x] 1.2 Add POSIX and Windows unit cases for global, project, temporary, source, and unknown installs.
|
||||
|
||||
## 2. Expose structured update-check outcomes
|
||||
|
||||
- [x] 2.1 Refactor the existing update check to return `available`, `current`, `disabled`, or `offline` with structured latest-version and update-guidance fields.
|
||||
- [x] 2.2 Keep `getAvailableCliUpdate()` as a compatibility wrapper so `openspec update` behavior does not change.
|
||||
- [x] 2.3 Cover privacy opt-outs, rejected registries, timeouts, invalid responses, current versions, and available updates without weakening existing network safeguards.
|
||||
|
||||
## 3. Add the version command
|
||||
|
||||
- [x] 3.1 Add `openspec version` with `--json` and `--check` options and no project-root prerequisite.
|
||||
- [x] 3.2 Emit one schema-versioned JSON document with explicit nulls for unknown values and no incidental stdout.
|
||||
- [x] 3.3 Add human-readable output for local information and each update-check status.
|
||||
- [x] 3.4 Reject `--upgrade` and other unsupported options without running an installer.
|
||||
|
||||
## 4. Verify compatibility and behavior
|
||||
|
||||
- [x] 4.1 Add CLI end-to-end coverage for text output, JSON output, `--check`, and execution outside an OpenSpec project.
|
||||
- [x] 4.2 Prove `openspec --version` still emits only the bare version string.
|
||||
- [x] 4.3 Prove JSON runs emit no telemetry notice, completion tip, color sequence, or extra stdout text.
|
||||
|
||||
## 5. Document and release
|
||||
|
||||
- [x] 5.1 Document `openspec version`, `--json`, and `--check` in `docs-lab/reference/cli.md` using the current docs-lab format; do not update the legacy `docs/` tree.
|
||||
- [x] 5.2 Add a minor changeset for `@fission-ai/openspec`.
|
||||
|
||||
## 6. Final verification
|
||||
|
||||
- [x] 6.1 Run `pnpm build`, the focused version-check and CLI tests, `pnpm test`, `pnpm exec tsc --noEmit`, and `pnpm lint`.
|
||||
- [x] 6.2 Run `openspec validate add-version-command --strict` and confirm every planning artifact is complete.
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-07-11
|
||||
@@ -0,0 +1,109 @@
|
||||
## Context
|
||||
|
||||
Grok Build (CLI binary `grok`) is not a Claude/Codex-style command-file adapter target. Its documented extension model is skill-centric:
|
||||
|
||||
- project skills from `./.grok/skills/` (walked up to the repo root)
|
||||
- user skills from `~/.grok/skills/`
|
||||
- plugin skills and optional `[skills] paths` in config
|
||||
- user-invocable skills appear as slash commands: `/<skill-name>`
|
||||
- core TUI commands (`/plan`, `/model`, `/skills`, …) are built-in, not project-generated files
|
||||
- no documented project-local `.grok/commands/` layout for custom OpenSpec command generation
|
||||
|
||||
Grok also has Claude/Cursor compatibility scanners that can free-ride existing `.claude/skills` or `.cursor/skills`. That is a personal workaround, not the product integration: OpenSpec should own a native `.grok` skills install so pure-Grok users and multi-tool projects get first-class init/update behavior.
|
||||
|
||||
OpenSpec already represents this shape:
|
||||
|
||||
- `AI_TOOLS` can advertise a `skillsDir`
|
||||
- `init`/`update` install skills for any selected tool with `skillsDir`
|
||||
- when command generation is attempted for a tool without an adapter, OpenSpec records `commandsSkipped`
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Add Grok Build using the same narrow skills-only pattern as Kimi CLI / ForgeCode / Mistral Vibe
|
||||
- Keep the implementation small: metadata, docs, focused regression test, changeset
|
||||
- Align specs with the existing adapterless code path
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- designing a Grok-specific command adapter without a documented project command-file surface
|
||||
- relying on Claude/Cursor free-ride as the supported integration
|
||||
- changing tool capability modeling or `delivery=commands` behavior for all adapterless tools (tracked in `add-tool-command-surface-capabilities`)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Represent Grok Build as an adapterless tool with `.grok`
|
||||
|
||||
Add a new `AI_TOOLS` entry:
|
||||
|
||||
```ts
|
||||
{ name: 'Grok Build', value: 'grok', available: true, successLabel: 'Grok Build', skillsDir: '.grok' }
|
||||
```
|
||||
|
||||
Rationale for IDs:
|
||||
|
||||
- `value: 'grok'` matches the CLI binary and the project directory `.grok` (same pattern as `claude` → `.claude`, `kimi` → `.kimi`)
|
||||
- display name `Grok Build` matches xAI product naming
|
||||
- alternatives considered: `grok-build` (product-accurate but inconsistent with other short tool IDs)
|
||||
|
||||
### 2. Do not add a Grok command adapter
|
||||
|
||||
No `src/core/command-generation/adapters/grok.ts`, and no registry change.
|
||||
|
||||
Rationale:
|
||||
|
||||
- skills are the documented custom extension surface and already become slash commands
|
||||
- inventing `.grok/commands/...` would create OpenSpec behavior that cannot be justified against xAI docs
|
||||
- existing adapterless path already skips command generation with an informational message
|
||||
|
||||
### 3. Document Grok by its real invocation surface
|
||||
|
||||
Grok docs in OpenSpec must use skill-name slash form:
|
||||
|
||||
- supported-tools: no generated command files; use skill-based `/openspec-*` invocations
|
||||
- commands / how-commands-work: examples such as `/openspec-propose`, `/openspec-apply-change`
|
||||
|
||||
Do not claim generated `opsx-*` files or Claude-style `/opsx:propose` as Grok's primary surface.
|
||||
|
||||
### 4. Treat Claude free-ride as out-of-scope workaround, not design
|
||||
|
||||
Grok can discover Claude skills when compat scanners are enabled. Native `.grok` support remains required because:
|
||||
|
||||
- pure Grok users may never select Claude
|
||||
- free-ride couples Grok to Claude layout and can be disabled via Grok config/env
|
||||
- OpenSpec update tracks configured tools by skillsDir presence; free-ride never registers Grok
|
||||
|
||||
If both Claude and Grok are configured, duplicate skill discovery is acceptable; `.grok` remains the canonical OpenSpec target for Grok Build.
|
||||
|
||||
### 5. Keep behavior aligned with current adapterless tools
|
||||
|
||||
- skills are created whenever delivery includes skills
|
||||
- command generation is skipped when no adapter exists
|
||||
- init output reports `Commands skipped for: grok (no adapter)`
|
||||
- update refreshes Grok when `.grok/skills/openspec-*` exists
|
||||
|
||||
## Test Strategy
|
||||
|
||||
Add one focused regression test in `test/core/init.test.ts`:
|
||||
|
||||
- configure `delivery=both`
|
||||
- run init with `--tools grok`
|
||||
- verify skills under `.grok/skills/...` (use `path.join` for expectations)
|
||||
- verify no `.grok/commands` directory is created
|
||||
- verify init log includes skipped command generation for `grok` with `(no adapter)` (use relaxed `.some()` matching, as in the Kimi follow-up commit)
|
||||
|
||||
That is enough because:
|
||||
|
||||
- adapterless update behavior already has generic coverage
|
||||
- CLI tool-id rendering is derived from `AI_TOOLS`
|
||||
- no command adapter or path-formatting logic is introduced
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Users confuse Claude free-ride with native support | Document native `.grok` path; optional brief note that Claude compat is separate |
|
||||
| `delivery=commands` still not capability-aware for skills-only tools | Accept same limitation as Kimi/ForgeCode/Vibe; capability work is separate |
|
||||
| Duplicate skills when both Claude and Grok selected | Acceptable; document that Grok may see both trees |
|
||||
| xAI later documents project command files | Skills-only remains correct today; adapter can be added later without breaking skills |
|
||||
@@ -0,0 +1,40 @@
|
||||
## Why
|
||||
|
||||
xAI Grok Build is a coding agent with a documented project skills root at `.grok/skills/`, and user-invocable skills surface as slash commands (`/<skill-name>`). OpenSpec does not yet list Grok Build as a supported tool, so users must free-ride on Claude/Cursor compat scanners or configure extra skill paths manually.
|
||||
|
||||
OpenSpec already supports adapterless skills-only tools (Kimi CLI, ForgeCode, Mistral Vibe). Grok Build should follow that pattern: install skills under `.grok/skills/` without inventing a command adapter for a project command-file surface that xAI docs do not define.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add Grok Build as a supported tool in `AI_TOOLS` with `value: 'grok'` and `skillsDir: '.grok'`
|
||||
- Document Grok Build as a skills-only integration (no generated `opsx-*` command files; invoke via `/openspec-*` skill names)
|
||||
- Align specs so `ai-tool-paths` and `cli-init` cover the Grok Build path and adapterless init behavior
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_None._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ai-tool-paths`: define the `.grok` skills root for Grok Build
|
||||
- `cli-init`: treat Grok Build as a supported adapterless selection that still generates skills and skips command-file generation
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/config.ts` - add Grok Build tool metadata
|
||||
- `docs/supported-tools.md` - add Grok Build row and tool id
|
||||
- `docs/commands.md` - document `/openspec-*` skill invocations for Grok Build
|
||||
- `docs/how-commands-work.md` - include Grok Build in slash-syntax table
|
||||
- `docs/cli.md` - include `grok` in the supported `--tools` list
|
||||
- `docs/troubleshooting.md` - list Grok Build among skills-only tools
|
||||
- `test/core/init.test.ts` - cover Grok Build as an adapterless tool during init
|
||||
- `.changeset/` - minor release note for the new tool
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Adding `src/core/command-generation/adapters/grok.ts`
|
||||
- Defining a `.grok/commands/...` output path
|
||||
- Relying on Claude/Cursor free-ride as the product integration
|
||||
- Changing the broader delivery model for adapterless tools under `delivery=commands` (tracked separately in `add-tool-command-surface-capabilities`)
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# ai-tool-paths Delta Specification
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Path configuration for supported tools
|
||||
|
||||
The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent Skills specification.
|
||||
|
||||
#### Scenario: Claude Code paths defined
|
||||
|
||||
- **WHEN** looking up the `claude` tool
|
||||
- **THEN** `skillsDir` SHALL be `.claude`
|
||||
|
||||
#### Scenario: Cursor paths defined
|
||||
|
||||
- **WHEN** looking up the `cursor` tool
|
||||
- **THEN** `skillsDir` SHALL be `.cursor`
|
||||
|
||||
#### Scenario: Windsurf paths defined
|
||||
|
||||
- **WHEN** looking up the `windsurf` tool
|
||||
- **THEN** `skillsDir` SHALL be `.windsurf`
|
||||
|
||||
#### Scenario: Kimi CLI paths defined
|
||||
|
||||
- **WHEN** looking up the `kimi` tool
|
||||
- **THEN** `skillsDir` SHALL be `.kimi`
|
||||
|
||||
#### Scenario: Grok Build paths defined
|
||||
|
||||
- **WHEN** looking up the `grok` tool
|
||||
- **THEN** `skillsDir` SHALL be `.grok`
|
||||
|
||||
#### Scenario: Tools without skillsDir
|
||||
|
||||
- **WHEN** a tool has no `skillsDir` defined
|
||||
- **THEN** skill generation SHALL error with message indicating the tool is not supported
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# cli-init Delta Specification
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Slash Command Generation
|
||||
|
||||
The command SHALL generate opsx slash commands only for selected tools that have a registered command adapter, while keeping adapterless tools valid for skill generation.
|
||||
|
||||
#### Scenario: Generating slash commands for a tool with a registered adapter
|
||||
|
||||
- **WHEN** a tool with a registered command adapter is selected during initialization
|
||||
- **THEN** create 9 slash command files using the tool's command adapter:
|
||||
- `/opsx:explore`
|
||||
- `/opsx:new`
|
||||
- `/opsx:continue`
|
||||
- `/opsx:apply`
|
||||
- `/opsx:ff`
|
||||
- `/opsx:verify`
|
||||
- `/opsx:sync`
|
||||
- `/opsx:archive`
|
||||
- `/opsx:bulk-archive`
|
||||
- **AND** use tool-specific path conventions (e.g., `.claude/commands/opsx/` for Claude)
|
||||
- **AND** include tool-specific frontmatter format
|
||||
|
||||
#### Scenario: Selected tool has no command adapter
|
||||
|
||||
- **GIVEN** a selected tool has `skillsDir` configured but no registered command adapter
|
||||
- **WHEN** initialization includes command generation
|
||||
- **THEN** skill generation for that tool SHALL still remain valid
|
||||
- **AND** command-file generation SHALL be skipped for that tool
|
||||
- **AND** the command output SHALL include `Commands skipped for: <tool-id> (no adapter)`
|
||||
|
||||
#### Scenario: Kimi CLI skips command-file generation
|
||||
|
||||
- **WHEN** the user selects Kimi CLI during initialization
|
||||
- **THEN** OpenSpec SHALL treat it as a supported tool with `skillsDir: '.kimi'`
|
||||
- **AND** command-file generation SHALL be skipped because no Kimi adapter is registered
|
||||
|
||||
#### Scenario: Grok Build skips command-file generation
|
||||
|
||||
- **WHEN** the user selects Grok Build during initialization
|
||||
- **THEN** OpenSpec SHALL treat it as a supported tool with `skillsDir: '.grok'`
|
||||
- **AND** command-file generation SHALL be skipped because no Grok adapter is registered
|
||||
@@ -0,0 +1,24 @@
|
||||
## 1. Tool Metadata
|
||||
|
||||
- [x] 1.1 Add `Grok Build` to `src/core/config.ts` with `value: 'grok'`, `successLabel: 'Grok Build'`, and `skillsDir: '.grok'` (alphabetically near related tools)
|
||||
|
||||
## 2. Documentation
|
||||
|
||||
- [x] 2.1 Update `docs/supported-tools.md` with a Grok Build row (`skillsDir` `.grok`, no command adapter; skill-based `/openspec-*` invocations) and add `grok` to the `--tools` list
|
||||
- [x] 2.2 Update `docs/commands.md` to document Grok Build skill invocations such as `/openspec-propose`, `/openspec-apply-change`
|
||||
- [x] 2.3 Update `docs/how-commands-work.md` slash-syntax table to include Grok Build (`/openspec-*` skill form)
|
||||
- [x] 2.4 Update `docs/cli.md` so the supported `--tools` list includes `grok`
|
||||
- [x] 2.5 Update `docs/troubleshooting.md` skills-only tool list to include Grok Build
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 Add a targeted init regression test for `--tools grok` with `delivery=both`: skills under `.grok/skills/...`, no `.grok/commands`, and commands-skipped log for `grok` `(no adapter)` using relaxed log matching and `path.join` expectations
|
||||
|
||||
## 4. Release Notes
|
||||
|
||||
- [x] 4.1 Add a changeset noting Grok Build as a supported skills-only tool via `.grok/skills/`
|
||||
|
||||
## 5. Validation
|
||||
|
||||
- [x] 5.1 Validate the change artifacts with `openspec validate add-grok-build-skills-only-support --strict` (or project-equivalent)
|
||||
- [x] 5.2 Run targeted tests (`test/core/init.test.ts` Grok case) and fix any regressions
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-15
|
||||
@@ -0,0 +1,86 @@
|
||||
## Context
|
||||
|
||||
See [proposal.md](proposal.md#why).
|
||||
|
||||
OpenSpec already routes every skill-capable tool through one pipeline: `AI_TOOLS` metadata in `src/core/config.ts` drives tool detection (`available-tools.ts`), selection and validation (`init.ts`), skill path resolution (`shared/skill-paths.ts`), generation, version drift, and update. Tools that expose no custom command files simply have no `ToolCommandAdapter`, which `command-surface.ts` classifies as capability `none`.
|
||||
|
||||
DeepSeek Harness parses skills from fixed local roots (see the [upstream filesystem provider](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/skill/skill-filesystem)): `<project>/.dsh/skills` (rank 100), `<project>/.agents/skills` (rank 200), and user-level `~/.dsh/skills` (rank 400). It discovers only one level (`<root>/<name>/SKILL.md` or `<root>/<name>.md`), requires `name` (kebab-case) and non-empty `description` frontmatter, tolerates extra fields, and exposes skills to the model through `<available_skills>` plus a `skill` tool; users can also trigger them with the `/name` gesture. OpenSpec's generated `SKILL.md` files already satisfy every dsh constraint, so no template or frontmatter changes are needed.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Add one `dsh` entry to `AI_TOOLS` that opts into the existing project-local skills pipeline.
|
||||
- Make first-time setup, auto-detection, refresh, and profile/delivery drift work through existing generic code.
|
||||
- Lock the dsh path and invocation behavior with focused tests.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- A dsh command adapter or any `.dsh/commands/` output — dsh has no file-based command surface.
|
||||
- A global `~/.dsh/skills` install target — dsh has a higher-priority project root and OpenSpec manages per-project artifacts.
|
||||
- Reclassifying dsh as `skills-invocable` in `command-surface.ts`; that belongs to the in-flight `add-tool-command-surface-capabilities` work. Until then dsh shares the current adapterless behavior of Rovo Dev CLI and Kimi Code.
|
||||
- Changing generated skill templates or frontmatter.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Represent dsh as an adapterless, project-local tool entry
|
||||
|
||||
Add to `src/core/config.ts`:
|
||||
|
||||
```ts
|
||||
{
|
||||
name: 'DeepSeek Harness',
|
||||
value: 'dsh',
|
||||
available: true,
|
||||
successLabel: 'DeepSeek Harness',
|
||||
skillsDir: '.dsh',
|
||||
},
|
||||
```
|
||||
|
||||
`resolveToolSkillsDir()` then resolves to `<projectRoot>/.dsh/skills`, which is dsh's rank-100 project root. Nothing else in init/update/selection needs a code change because those paths derive from `AI_TOOLS`.
|
||||
|
||||
Alternative considered: write to `~/.dsh/skills` via `globalSkillsDir`. Rejected because the project root outranks the user root, keeps artifacts repo-local and reviewable, and matches OpenSpec's project-scoped update/removal semantics (MiniMax Code's global-only design exists to work around a tool that only reads the user root, which is not dsh's case).
|
||||
|
||||
### 2. Detect dsh from its `.dsh` directory
|
||||
|
||||
Use the existing `skillsDir` detection, which requires a directory. This recognizes both a bare `.dsh` project root and a populated `.dsh/skills` tree, but rejects a regular file named `.dsh`.
|
||||
|
||||
Explicit `detectionPaths` are unnecessary: `.dsh/skills` already implies a `.dsh` directory, and overrides accept file signals for tools that need them. Auto-detection identifies a tool root; it does not guarantee every child path is writable. A regular file at `.dsh/skills` remains a filesystem conflict reported during generation, as for other directory-based tools.
|
||||
|
||||
### 3. No command adapter; inherit capability `none`
|
||||
|
||||
`resolveCommandSurfaceCapability('dsh')` returns `none` because no adapter is registered. Consequences, all existing generic behavior:
|
||||
|
||||
- `delivery=both` / `skills`: skills generated; init reports `Commands skipped for: dsh (no adapter)`.
|
||||
- `delivery=commands`: no dsh artifacts and the existing zero-artifact correction is printed.
|
||||
|
||||
Alternative considered: special-case dsh as `skills-invocable` like Codex so commands-only delivery keeps skills. Semantically dsh's skill tool + `/name` gesture are invocable, but the current shipped model only special-cases Codex; widening it here would duplicate the open `add-tool-command-surface-capabilities` change and expand this change's test matrix. Deferred deliberately.
|
||||
|
||||
### 4. Use the default `/openspec-*` skill reference spelling
|
||||
|
||||
dsh's user-facing `/name` gesture makes `/openspec-propose` a real, typeable invocation, so the default transformer (`getSkillReferenceTransformer` fallback) is correct. The model side can call the `skill` tool by name regardless.
|
||||
|
||||
Alternative considered: add `dsh` to `NATURAL_LANGUAGE_SKILL_TOOLS` (like Rovo). Rejected because Rovo has no slash-like gesture at all, while dsh documents `/name`.
|
||||
|
||||
### 5. No shared-root ownership work
|
||||
|
||||
`.dsh/skills` is used by no other `AI_TOOLS` entry, so `shared-skill-target.ts` marker/reconciliation logic does not apply. If the same repo also generates the `.agents` target, dsh will prefer its rank-100 `.dsh/skills` tree and there is no single-writer conflict to resolve.
|
||||
|
||||
### 6. No frontmatter or template changes
|
||||
|
||||
OpenSpec writes `---` first line, kebab-case `name`, non-empty `description`, one-level `<name>/SKILL.md`, and extra fields such as `license`, `compatibility`, and `metadata`. The upstream parser accepts these extra fields. Tests parse every generated skill's YAML frontmatter and check required names and descriptions.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Commands-only delivery leaves dsh with zero artifacts] → Mitigation: init/update already print the existing `delivery` correction for capability-`none` tools; docs list dsh as skills-only, and the deferred capability work is the real fix.
|
||||
- [`.dsh` detection can fire on a stale empty directory after commands-only removal] → Mitigation: interactive init shows detected-but-unconfigured tools as unselected in extend mode; behavior matches Rovo and is a cosmetic pre-selection, never a forced write.
|
||||
- [dsh fail-closed parsing could silently drop skills] → Mitigation: generated files already comply; the init regression test checks frontmatter shape, and manual smoke testing against a real dsh session is in tasks.
|
||||
- [Same-name skills under `.dsh/skills` and `.agents/skills`] → Mitigation: dsh's rank ordering (100 < 200) deterministically prefers `.dsh/skills`; this is upstream behavior, documented in supported-tools.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No data migration is required. Reverting the entry stops future dsh detection and generation but leaves existing `.dsh/skills` files in user projects. Remove the generated `openspec-*` folders separately if rollback is needed; preserve user-authored skills. Projects using the shared `.agents` target today keep working; selecting `dsh` on a later `openspec init` writes the dedicated higher-priority root without touching `.agents`.
|
||||
|
||||
## Open Questions
|
||||
|
||||
_None._
|
||||
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
DeepSeek Harness discovers skills from fixed local roots, with `<project>/.dsh/skills` as its highest-priority project root. OpenSpec supports many assistants but has no dedicated target for it today, so dsh users can only use the vendor-neutral shared `.agents` target or hand-place skills — losing the dedicated `.dsh` integration.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add DeepSeek Harness as a supported tool with id `dsh`, `skillsDir: '.dsh'`, and directory-based auto-detection from `.dsh`.
|
||||
- Generate the OpenSpec workflow skills into `.dsh/skills/openspec-*/SKILL.md` for dsh via `openspec init --tools dsh` and `openspec update`.
|
||||
- Keep dsh skills-only: no command adapter and no `.dsh/commands/` files, because dsh has no file-based custom command surface.
|
||||
- Spell dsh skill references as `/openspec-*` (dsh supports the user `/name` gesture), matching the existing skills-only tool pattern.
|
||||
- Document dsh in the supported tools and command syntax docs.
|
||||
- Add regression tests for detection, path resolution, init, update, and invocation spelling.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_None._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ai-tool-paths`: define the `.dsh` skills root and directory-based detection for DeepSeek Harness.
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/config.ts` — add the `dsh` entry to `AI_TOOLS`
|
||||
- `docs/supported-tools.md` — tool row, invocation table, and `--tools` id list
|
||||
- `docs/cli.md` — supported `--tools` id list
|
||||
- `docs/commands.md`, `docs/how-commands-work.md`, `docs/troubleshooting.md` — skills-only invocation tables and notes
|
||||
- `test/core/available-tools.test.ts`, `test/core/shared/skill-paths.test.ts`, `test/core/shared/tool-detection.test.ts`, `test/core/init.test.ts`, `test/core/update.test.ts`, `test/utils/command-references.test.ts`, `test/core/command-generation/registry.test.ts` — targeted dsh coverage
|
||||
- `.changeset/add-dsh-support.md` — release note
|
||||
@@ -0,0 +1,47 @@
|
||||
# ai-tool-paths Delta Specification
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Path configuration for supported tools
|
||||
|
||||
The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent Skills specification.
|
||||
|
||||
#### Scenario: Claude Code paths defined
|
||||
|
||||
- **WHEN** looking up the `claude` tool
|
||||
- **THEN** `skillsDir` SHALL be `.claude`
|
||||
|
||||
#### Scenario: Cursor paths defined
|
||||
|
||||
- **WHEN** looking up the `cursor` tool
|
||||
- **THEN** `skillsDir` SHALL be `.cursor`
|
||||
|
||||
#### Scenario: Windsurf paths defined
|
||||
|
||||
- **WHEN** looking up the `windsurf` tool
|
||||
- **THEN** `skillsDir` SHALL be `.windsurf`
|
||||
|
||||
#### Scenario: Kimi Code paths defined
|
||||
|
||||
- **WHEN** looking up the `kimi` tool
|
||||
- **THEN** `skillsDir` SHALL be `.kimi-code`
|
||||
- **AND** OpenSpec-managed skills remaining under the legacy `.kimi/skills` directory SHALL be migrated to `.kimi-code/skills` during init and update, preserving user files
|
||||
|
||||
#### Scenario: Hermes Agent paths defined
|
||||
|
||||
- **WHEN** looking up the `hermes` tool
|
||||
- **THEN** `skillsDir` SHALL be `.hermes`
|
||||
- **AND** `setupNote` SHALL explain that project `.hermes/skills` must be added to `skills.external_dirs` in `~/.hermes/config.yaml`
|
||||
- **AND** `openspec init` and `openspec update` SHALL display the note whenever `hermes` is configured
|
||||
|
||||
#### Scenario: DeepSeek Harness paths defined
|
||||
|
||||
- **WHEN** looking up the `dsh` tool
|
||||
- **THEN** `skillsDir` SHALL be `.dsh`
|
||||
- **AND** auto-detection SHALL require `.dsh` to be a directory
|
||||
- **AND** OpenSpec SHALL write dsh skills under `<projectRoot>/.dsh/skills/` using platform-native path joining
|
||||
|
||||
#### Scenario: Tools without skillsDir
|
||||
|
||||
- **WHEN** a tool has no `skillsDir` defined
|
||||
- **THEN** skill generation SHALL error with message indicating the tool is not supported
|
||||
@@ -0,0 +1,30 @@
|
||||
## 1. Tool Metadata
|
||||
|
||||
- [x] 1.1 Add the `DeepSeek Harness` entry to `AI_TOOLS` in `src/core/config.ts` with `value: 'dsh'` and `skillsDir: '.dsh'`, using the existing directory-based detection
|
||||
- [x] 1.2 Verify no other production code changes are required: init selection, `--tools` help, command surface capability, update drift, and shared-root handling must all derive from the new metadata
|
||||
|
||||
## 2. Detection and Path Tests
|
||||
|
||||
- [x] 2.1 Add `test/core/available-tools.test.ts` cases: detect `dsh` from `.dsh/skills` and from a bare `.dsh` directory; do not detect when neither exists or `.dsh` is a regular file
|
||||
- [x] 2.2 Add a `test/core/shared/skill-paths.test.ts` case resolving `dsh` to `path.join(root, '.dsh', 'skills')`
|
||||
- [x] 2.3 Add `test/core/shared/tool-detection.test.ts` cases: `getToolsWithSkillsDir()` includes `dsh`; skill status and configured-tool detection work for `.dsh/skills/openspec-*/SKILL.md`
|
||||
|
||||
## 3. Generation and Update Tests
|
||||
|
||||
- [x] 3.1 Add an `InitCommand` regression in `test/core/init.test.ts`: `--tools dsh` writes `.dsh/skills/openspec-explore/SKILL.md`, creates no `.dsh/commands`, logs the no-adapter skip, uses `/openspec-*` references in skill bodies and the getting-started hint, and the generated frontmatter satisfies dsh parsing (leading `---`, kebab-case name, non-empty description)
|
||||
- [x] 3.2 Add an `UpdateCommand` regression in `test/core/update.test.ts`: refresh a stale dsh skill and verify a second update is idempotent
|
||||
- [x] 3.3 Add `test/utils/command-references.test.ts` coverage that dsh uses the default `/openspec-*` form, and `test/core/command-generation/registry.test.ts` coverage that dsh has no command adapter
|
||||
|
||||
## 4. Documentation
|
||||
|
||||
- [x] 4.1 Update `docs/supported-tools.md`: add the dsh tool row, add dsh to the skills-only invocation row and the `--tools` id list, and explain that dsh reads `.dsh/skills` at higher priority than `.agents/skills`
|
||||
- [x] 4.2 Update the supported `--tools` id list in `docs/cli.md`
|
||||
- [x] 4.3 Update the skills-only syntax tables in `docs/commands.md` and `docs/how-commands-work.md`, and the skills-only tool list in `docs/troubleshooting.md`
|
||||
|
||||
## 5. Release and Validation
|
||||
|
||||
- [x] 5.1 Add `.changeset/add-dsh-support.md` with a minor bump describing `openspec init --tools dsh`
|
||||
- [x] 5.2 Run `pnpm run lint`, `pnpm run build`, and the targeted vitest files for detection, paths, init, update, and command references
|
||||
- [x] 5.3 Run the full test suite (`pnpm test`) and confirm cross-platform path assertions pass on Windows (no hardcoded separators in new tests)
|
||||
- [x] 5.4 Run `openspec validate` for this change and fix any spec or change validation issues
|
||||
- [x] 5.5 Manual smoke test in a temporary git project: `openspec init --tools dsh`, confirm `.dsh/skills/openspec-*/SKILL.md` files, start a dsh session and confirm the skills appear in the catalog and load via the skill tool or `/openspec-propose`
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-02
|
||||
@@ -0,0 +1,28 @@
|
||||
# Release archive claims on Windows
|
||||
|
||||
## Why
|
||||
|
||||
`openspec archive` creates `.openspec-archive.lock` before moving a change into
|
||||
the archive. On Windows, a successful archive can leave that lock behind because
|
||||
the cleanup check compares the device id returned by the open file handle with
|
||||
the one returned by `fs.lstat()`. Node reports a real device id from the handle
|
||||
and `0n` from the path stat on the affected Windows/NTFS setup, so the ownership
|
||||
check never passes.
|
||||
|
||||
The archive itself succeeds, but the next archive is blocked by the stale claim
|
||||
and the user has to delete `.openspec-archive.lock` by hand.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Treat an inode match plus matching claim contents as sufficient when either
|
||||
side reports `dev: 0n`, while still requiring the two path stats around the
|
||||
read to match.
|
||||
- Keep the existing protection against deleting a claim that was replaced by
|
||||
another process.
|
||||
- Add a regression test that simulates the Windows path-stat device id behavior.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected spec: `cli-archive`
|
||||
- Affected code: `src/core/archive.ts`
|
||||
- Affected tests: `test/core/archive.test.ts`
|
||||
@@ -0,0 +1,36 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Archive Process
|
||||
|
||||
The archive operation SHALL follow a structured process to safely move changes to the archive.
|
||||
|
||||
#### Scenario: Performing archive
|
||||
|
||||
- **WHEN** archiving a change
|
||||
- **THEN** execute these steps:
|
||||
1. Create archive/ directory if it doesn't exist
|
||||
2. Generate target name as `YYYY-MM-DD-[change-name]` using current date, keeping the name as-is when it already starts with a `YYYY-MM-DD-` prefix
|
||||
3. Claim the target and verify that it does not already exist
|
||||
4. Prepare and validate spec updates from the active change's delta specs
|
||||
5. Apply the spec updates as a rollback-capable transaction
|
||||
6. Move the entire change directory to the archive location
|
||||
7. If a spec mutation or final move fails before a complete archive is secured, restore the spec transaction and leave or return the change at its active path
|
||||
8. If a verified fallback copy completes but staged-source cleanup fails, retain the complete archive and committed spec state for recovery instead of risking the only complete copy
|
||||
|
||||
#### Scenario: Archive already exists
|
||||
|
||||
- **WHEN** target archive already exists
|
||||
- **THEN** fail with error message
|
||||
- **AND** do not overwrite existing archive
|
||||
|
||||
#### Scenario: Successful archive
|
||||
|
||||
- **WHEN** move succeeds
|
||||
- **THEN** display success message with archived name and list of updated specs
|
||||
|
||||
#### Scenario: Successful archive releases its own claim
|
||||
|
||||
- **WHEN** an archive run successfully moves a change to its archive destination
|
||||
- **THEN** remove the temporary archive claim it created
|
||||
- **AND** do so on supported platforms even when a path stat does not report a device id
|
||||
- **AND** never remove a claim whose path identity or contents changed before cleanup
|
||||
@@ -0,0 +1,12 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Release owned claims cross-platform
|
||||
- [x] 1.1 Compare archive claim files by inode and tolerate a missing device id from either stat result
|
||||
- [x] 1.2 Preserve the content and repeated-path-stat checks before unlinking
|
||||
|
||||
## 2. Verify behavior
|
||||
- [x] 2.1 Add regression coverage for the Windows `dev: 0n` path-stat case
|
||||
- [x] 2.2 Run the focused archive regression test
|
||||
|
||||
## 3. Record behavior
|
||||
- [x] 3.1 Add a `cli-archive` spec delta for successful claim cleanup
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-25
|
||||
@@ -0,0 +1,25 @@
|
||||
## Why
|
||||
|
||||
OpenSpec labels the `bob` integration as "Bob Shell," but the same `.bob` configuration root serves the IBM Bob product. The narrower name makes the tool picker and status output look limited to the CLI.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Rename the `bob` tool entry and success label to "IBM Bob."
|
||||
- Update the supported-tools reference to use the product name.
|
||||
- Preserve `.bob/commands/` generation for Bob Shell, which still supports custom slash commands.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- None.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ai-tool-paths`: Use "IBM Bob" as the user-facing name for the `bob` integration.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected code: `src/core/config.ts` and its tool-detection test.
|
||||
- Affected docs: `docs-lab/reference/supported-tools.md`.
|
||||
- Command and skill paths do not change.
|
||||
@@ -0,0 +1,12 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: IBM Bob tool identity
|
||||
|
||||
The `AI_TOOLS` entry for `bob` SHALL use the IBM Bob product name without changing its skill or command paths.
|
||||
|
||||
#### Scenario: IBM Bob paths and display name
|
||||
|
||||
- **WHEN** looking up the `bob` tool
|
||||
- **THEN** `name` and `successLabel` SHALL be `IBM Bob`
|
||||
- **AND** `skillsDir` SHALL be `.bob`
|
||||
- **AND** generated commands SHALL remain under `.bob/commands/`
|
||||
@@ -0,0 +1,6 @@
|
||||
## 1. Implementation
|
||||
|
||||
- [x] 1.1 Rename the `bob` tool entry and success label to "IBM Bob."
|
||||
- [x] 1.2 Keep the Bob command adapter and existing command paths unchanged.
|
||||
- [x] 1.3 Update the docs-lab supported-tools reference.
|
||||
- [x] 1.4 Cover the user-facing name in a tool-detection test.
|
||||
@@ -56,6 +56,31 @@ The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent
|
||||
- **AND** `setupNote` SHALL explain that project `.hermes/skills` must be added to `skills.external_dirs` in `~/.hermes/config.yaml`
|
||||
- **AND** `openspec init` and `openspec update` SHALL display the note whenever `hermes` is configured
|
||||
|
||||
#### Scenario: DeepSeek Harness paths defined
|
||||
|
||||
- **WHEN** looking up the `dsh` tool
|
||||
- **THEN** `skillsDir` SHALL be `.dsh`
|
||||
- **AND** auto-detection SHALL require `.dsh` to be a directory
|
||||
- **AND** OpenSpec SHALL write dsh skills under `<projectRoot>/.dsh/skills/` using platform-native path joining
|
||||
|
||||
#### Scenario: Grok Build paths defined
|
||||
|
||||
- **WHEN** looking up the `grok` tool
|
||||
- **THEN** `skillsDir` SHALL be `.grok`
|
||||
|
||||
#### Scenario: Warp paths and detection defined
|
||||
|
||||
- **WHEN** looking up the `warp` tool
|
||||
- **THEN** `skillsDir` SHALL be `.warp`
|
||||
- **AND** `detectionPaths` SHALL include `.warp` and `WARP.md`
|
||||
|
||||
#### Scenario: Warp invokes skills without command files
|
||||
|
||||
- **WHEN** generating workflows for the `warp` tool with delivery set to `commands`
|
||||
- **THEN** skills SHALL remain installed in `.warp/skills/`
|
||||
- **AND** no command adapter or command files SHALL be required
|
||||
- **AND** each skill SHALL be directly invocable by its `/openspec-*` name
|
||||
|
||||
#### Scenario: Tools without skillsDir
|
||||
|
||||
- **WHEN** a tool has no `skillsDir` defined
|
||||
|
||||
@@ -53,6 +53,10 @@ The system SHALL compute a valid topological build order for artifacts.
|
||||
### Requirement: State Detection
|
||||
The system SHALL detect artifact completion state by scanning the filesystem.
|
||||
|
||||
The system SHALL recognize `generates` values containing `*`, `?`, or `[` as glob patterns. It SHALL also support brace alternatives, brace ranges, and the `@()`, `+()`, `!()`, `*()`, and `?()` extglob forms. An artifact with a glob output SHALL be completed when at least one matching file exists.
|
||||
|
||||
The system SHALL preserve literal filenames with a bare leading `!`, plain parentheses, or single-element braces when no supported glob syntax is present. Brace expansion SHALL preserve literal brace groups and recognize later and nested expansion groups. Expanded output paths and traversed symbolic links SHALL remain within the change directory.
|
||||
|
||||
#### Scenario: Simple file exists
|
||||
- **WHEN** an artifact generates "proposal.md" and the file exists
|
||||
- **THEN** the artifact is marked as completed
|
||||
@@ -73,6 +77,48 @@ The system SHALL detect artifact completion state by scanning the filesystem.
|
||||
- **WHEN** the change directory does not exist
|
||||
- **THEN** all artifacts are marked as not completed (empty state)
|
||||
|
||||
#### Scenario: Brace alternatives with matching files
|
||||
- **WHEN** an artifact generates "review-{api,ui}.md" and "review-api.md" exists
|
||||
- **THEN** the artifact is marked as completed
|
||||
|
||||
#### Scenario: Brace range after a literal brace group
|
||||
- **WHEN** an artifact generates "report-{draft}-{1..3}.md"
|
||||
- **AND** "report-{draft}-1.md", "report-{draft}-2.md", "report-{draft}-3.md", and "report-{draft}-4.md" exist
|
||||
- **THEN** its resolved outputs contain exactly the first three files
|
||||
- **AND** the artifact is marked as completed
|
||||
|
||||
#### Scenario: Later and nested brace alternatives
|
||||
- **WHEN** an artifact generates "report-{draft}-{{api},ui}.md" and "report-{draft}-{api}.md" exists
|
||||
- **THEN** the artifact is marked as completed
|
||||
|
||||
#### Scenario: Extglob alternatives with matching files
|
||||
- **WHEN** an artifact generates "@(proposal|design).md" or "+(proposal|design).md" and "proposal.md" exists
|
||||
- **THEN** the artifact is marked as completed
|
||||
|
||||
#### Scenario: Negative extglob excludes its alternatives
|
||||
- **WHEN** an artifact generates "!(proposal|design).md"
|
||||
- **AND** "proposal.md", "design.md", and "notes.md" exist
|
||||
- **THEN** its resolved outputs contain only "notes.md"
|
||||
- **AND** the artifact is marked as completed
|
||||
|
||||
#### Scenario: Brace or extglob pattern without matching files
|
||||
- **WHEN** an artifact generates "review-{api,ui}.md" or "@(proposal|design).md" and no matching files exist
|
||||
- **THEN** the artifact is not marked as completed
|
||||
|
||||
#### Scenario: Literal output names remain literal
|
||||
- **WHEN** an artifact generates "!review.md", "(proposal|design).md", or "review-{api}.md"
|
||||
- **THEN** completion depends on the existence of a file with that exact name
|
||||
|
||||
#### Scenario: Brace expansion escapes the change directory
|
||||
- **WHEN** an artifact generates "{safe,../outside}/review.md"
|
||||
- **THEN** output resolution rejects the expanded path outside the change directory before matching files
|
||||
- **AND** rejection does not depend on whether the outside file exists
|
||||
|
||||
#### Scenario: Expanded directory pattern reaches an outbound symbolic link
|
||||
- **WHEN** an artifact generates "content/{safe,linked}/review.md" or "content/@(safe|linked)/review.md"
|
||||
- **AND** "content/linked" is a symbolic link to a directory outside the change directory
|
||||
- **THEN** output resolution rejects traversal through that link even when no matching files exist
|
||||
|
||||
### Requirement: Ready Artifact Query
|
||||
The system SHALL identify which artifacts are ready to be created based on dependency completion.
|
||||
|
||||
@@ -137,4 +183,3 @@ The system SHALL support self-contained schema directories with co-located templ
|
||||
#### Scenario: List available schemas
|
||||
- **WHEN** listing schemas
|
||||
- **THEN** the system returns schema names from both user and package directories
|
||||
|
||||
|
||||
@@ -183,17 +183,6 @@ The system SHALL provide consistent output formatting.
|
||||
- **WHEN** loading change state takes time
|
||||
- **THEN** the system displays a spinner during loading
|
||||
|
||||
### Requirement: Experimental Isolation
|
||||
The system SHALL implement artifact workflow commands in isolation for easy removal.
|
||||
|
||||
#### Scenario: Single file implementation
|
||||
- **WHEN** artifact workflow feature is implemented
|
||||
- **THEN** all commands are in `src/commands/artifact-workflow.ts`
|
||||
|
||||
#### Scenario: Help text marking
|
||||
- **WHEN** user runs `--help` on any artifact workflow command
|
||||
- **THEN** help text indicates the command is experimental
|
||||
|
||||
### Requirement: Schema Apply Block
|
||||
|
||||
The system SHALL support an `apply` block in schema definitions that controls when and how implementation begins.
|
||||
|
||||
@@ -182,9 +182,9 @@ The `openspec config profile` command SHALL provide an action-first interactive
|
||||
|
||||
- **WHEN** user runs `openspec config profile` interactively
|
||||
- **THEN** the first prompt SHALL offer:
|
||||
- `Change delivery + workflows`
|
||||
- `Change delivery only`
|
||||
- `Change workflows only`
|
||||
- `Delivery and workflows`
|
||||
- `Delivery only`
|
||||
- `Workflows only`
|
||||
- `Keep current settings (exit)`
|
||||
|
||||
#### Scenario: Delivery prompt marks current selection
|
||||
|
||||
@@ -231,6 +231,12 @@ The command SHALL generate opsx slash commands only for selected tools that have
|
||||
- **THEN** OpenSpec SHALL treat it as a supported tool with `skillsDir: '.kimi-code'`
|
||||
- **AND** command-file generation SHALL be skipped because no Kimi adapter is registered
|
||||
|
||||
#### Scenario: Grok Build skips command-file generation
|
||||
|
||||
- **WHEN** the user selects Grok Build during initialization
|
||||
- **THEN** OpenSpec SHALL treat it as a supported tool with `skillsDir: '.grok'`
|
||||
- **AND** command-file generation SHALL be skipped because no Grok adapter is registered
|
||||
|
||||
### Requirement: Config File Generation
|
||||
|
||||
The command SHALL create an OpenSpec config file with schema settings.
|
||||
|
||||
@@ -117,7 +117,7 @@ The update command SHALL refresh existing slash command files for configured too
|
||||
- **AND** skip creating missing files (the update command only refreshes what already exists)
|
||||
|
||||
#### Scenario: Updating slash commands for Kilo Code
|
||||
- **WHEN** `.kilocode/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
|
||||
- **WHEN** `.kilo/command/` contains OpenSpec-managed `opsx-*.md` command files for the configured profile
|
||||
- **THEN** refresh each file using shared templates wrapped in OpenSpec markers
|
||||
- **AND** ensure templates include instructions for the relevant workflow stage
|
||||
- **AND** skip creating missing files (the update command only refreshes what already exists)
|
||||
|
||||
@@ -45,6 +45,35 @@ The dashboard SHALL show active changes with visual progress indicators.
|
||||
- **AND** treat missing progress values as 0% for ordering
|
||||
- **AND** break ties by change identifier in ascending alphabetical order to keep output deterministic
|
||||
|
||||
### Requirement: Active Change Workflow Status
|
||||
|
||||
The dashboard SHALL show each active change's schema and artifact states beneath its task progress, using the same workflow resolution as `openspec status`. Workflow status SHALL NOT change task progress, change categories, or sorting.
|
||||
|
||||
#### Scenario: Workflow states
|
||||
|
||||
- **WHEN** an active change's workflow can be loaded
|
||||
- **THEN** show the schema name and artifacts in dependency order
|
||||
- **AND** mark existing artifact outputs with `✓`, ready artifacts with `→`, blocked artifacts with no symbol, and skipped artifacts with `(skipped)`
|
||||
- **AND** treat an existing tasks artifact as done even when its implementation checklist is unfinished
|
||||
|
||||
#### Scenario: Store-local workflow
|
||||
|
||||
- **WHEN** the dashboard targets a store through `--store` or a project store pointer
|
||||
- **THEN** resolve workflow schemas and artifact files from that store
|
||||
- **AND** use the store's default schema for changes without a schema in their metadata
|
||||
|
||||
#### Scenario: Invalid workflow
|
||||
|
||||
- **WHEN** an active change's metadata or schema cannot be loaded
|
||||
- **THEN** print a warning identifying the change and the error
|
||||
- **AND** omit only that change's workflow status while retaining its task progress and rendering other changes
|
||||
|
||||
#### Scenario: Terminal controls in workflow text
|
||||
|
||||
- **WHEN** schema names, artifact identifiers, or workflow errors contain terminal control characters
|
||||
- **THEN** replace those characters with inert text in the dashboard output
|
||||
- **AND** preserve the underlying identifiers and workflow states
|
||||
|
||||
### Requirement: Completed Changes Display
|
||||
|
||||
The dashboard SHALL list completed changes in a separate section, only showing changes with ALL tasks completed.
|
||||
@@ -126,4 +155,3 @@ The dashboard SHALL display changes without tasks in a separate "Draft" section.
|
||||
|
||||
- **WHEN** multiple draft changes exist
|
||||
- **THEN** system sorts them alphabetically by name
|
||||
|
||||
|
||||
@@ -32,6 +32,13 @@ The system SHALL detect legacy OpenSpec artifacts from previous init versions.
|
||||
- `.windsurf/workflows/openspec-*.md`
|
||||
- And equivalent directories for all tools in the legacy SlashCommandRegistry
|
||||
|
||||
#### Scenario: Detecting legacy Kilo Code workflows
|
||||
|
||||
- **WHEN** `.kilocode/workflows/` contains OpenSpec-managed `opsx-*.md` or `openspec-*.md` workflow files
|
||||
- **THEN** `openspec init` or legacy cleanup SHALL remove those files
|
||||
- **AND** Kilo Code commands SHALL be generated under `.kilo/command/`
|
||||
- **AND** `openspec update` SHALL NOT refresh files that remain only under `.kilocode/workflows/`
|
||||
|
||||
#### Scenario: Detecting legacy OpenSpec structure files
|
||||
|
||||
- **WHEN** running `openspec init` on an existing project
|
||||
@@ -160,4 +167,3 @@ The system SHALL report what was cleaned up.
|
||||
- **WHEN** no legacy artifacts are found
|
||||
- **THEN** the system SHALL NOT display the cleanup section
|
||||
- **AND** proceed directly with skill setup
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ Requirement headers SHALL serve as unique identifiers for programmatic matching
|
||||
|
||||
- **WHEN** processing delta changes
|
||||
- **THEN** use the `### Requirement: [Name]` header as the unique identifier
|
||||
- **AND** match using normalized headers: `normalize(header) = trim(header)`
|
||||
- **AND** compare headers with case-sensitive equality after normalization
|
||||
- **AND** strip a closing run of `#` characters when a space or tab precedes it and only spaces or tabs follow it, then trim surrounding whitespace
|
||||
- **AND** compare normalized requirement names with case-sensitive equality
|
||||
|
||||
#### Scenario: Handling requirement renames
|
||||
|
||||
|
||||
@@ -45,27 +45,37 @@ The skill SHALL check artifact completion status using the artifact graph before
|
||||
|
||||
### Requirement: Task Completion Check
|
||||
|
||||
The skill SHALL check task completion status from tasks.md before archiving.
|
||||
The skill SHALL check the selected change's task completion using `totalTasks` and `completedTasks` from `openspec list --json`, with the same selected-root flags used for the rest of the workflow. It SHALL match the change by name and use the CLI's schema-aware task resolution in both single and bulk archive workflows.
|
||||
|
||||
#### Scenario: Incomplete tasks found
|
||||
|
||||
- **WHEN** agent reads tasks.md
|
||||
- **AND** incomplete tasks are found (marked with `- [ ]`)
|
||||
- **WHEN** the selected change has `totalTasks` greater than `completedTasks`
|
||||
- **THEN** display warning showing count of incomplete tasks
|
||||
- **AND** prompt user for confirmation to continue
|
||||
- **AND** proceed if user confirms
|
||||
|
||||
#### Scenario: All tasks complete
|
||||
|
||||
- **WHEN** agent reads tasks.md
|
||||
- **AND** all tasks are complete (marked with `- [x]`)
|
||||
- **WHEN** the selected change has equal `totalTasks` and `completedTasks`
|
||||
- **THEN** proceed without task-related warning
|
||||
|
||||
#### Scenario: No tasks file
|
||||
#### Scenario: No tracked tasks
|
||||
|
||||
- **WHEN** tasks.md does not exist
|
||||
- **WHEN** the CLI reports `totalTasks` as zero for the selected change
|
||||
- **THEN** proceed without task-related warning
|
||||
|
||||
#### Scenario: Custom task artifact or output path
|
||||
|
||||
- **WHEN** the schema tracks tasks under a custom artifact name, output path, or glob
|
||||
- **THEN** use the CLI totals across the schema-resolved files
|
||||
- **AND** do not infer completion from artifact existence, an artifact id of `tasks`, or the absence of a top-level `tasks.md`
|
||||
|
||||
#### Scenario: Task progress lookup unavailable
|
||||
|
||||
- **WHEN** the list command fails, returns invalid JSON, omits or duplicates a selected change, or reports invalid task counts
|
||||
- **THEN** report the lookup problem and stop before syncing or archiving
|
||||
- **AND** do not treat the missing progress as zero tasks
|
||||
|
||||
### Requirement: Spec Sync Prompt
|
||||
|
||||
The skill SHALL prompt to sync delta specs before archiving if specs exist.
|
||||
@@ -78,7 +88,8 @@ The skill SHALL prompt to sync delta specs before archiving if specs exist.
|
||||
- **AND** if user cancels, stop without archiving
|
||||
- **AND** if user confirms, execute `/opsx:sync` logic inline and wait for it to complete
|
||||
- **AND** verify every capability that has a delta spec, not only those the sync reports it touched: ADDED requirements present, MODIFIED requirements carrying the changes named in the delta, REMOVED requirements absent, RENAMED requirements present under the new name and absent under the old one
|
||||
- **AND** treat a capability whose last requirement the sync removed as verified when its main spec was deleted rather than left empty, and a spec the sync deliberately kept and reported as verified too
|
||||
- **AND** treat a capability whose last requirement the sync removed as verified when its main spec was deleted rather than left empty
|
||||
- **AND** treat any stop or blocking condition the sync reports as a failed sync, including a main spec it left unmodified because a retirement was blocked
|
||||
- **AND** stop without archiving if the sync fails or any capability does not verify
|
||||
- **AND** archive only after verification passes, or when the user explicitly chose to archive without syncing or to archive already-synced specs
|
||||
|
||||
|
||||
@@ -15,34 +15,51 @@ The system SHALL provide an `/opsx:verify` skill that validates implementation a
|
||||
#### Scenario: Verify without change name
|
||||
- **WHEN** agent executes `/opsx:verify` without a change name
|
||||
- **THEN** the agent infers the change from conversation context, or auto-selects it when only one active change exists
|
||||
- **AND** when ambiguous, prompts user to select from available changes, showing only changes that have implementation tasks
|
||||
- **AND** when ambiguous, prompts user to select from all active changes, including changes with no tracked tasks
|
||||
- **AND** announces which change was selected and how to override
|
||||
|
||||
#### Scenario: Change has no tasks
|
||||
- **WHEN** selected change has no tasks.md or tasks are empty
|
||||
- **THEN** the agent reports "No tasks to verify"
|
||||
- **AND** suggests running `/opsx:continue` to create tasks
|
||||
#### Scenario: Change has no task descriptions
|
||||
- **WHEN** the schema configures task tracking but the structured task list provides no usable task descriptions, even if task progress reports nonzero totals
|
||||
- **THEN** the agent reports Task Completion as not verified with the reason
|
||||
- **AND** continues checks supported by the remaining artifacts
|
||||
|
||||
#### Scenario: Schema has no task tracking
|
||||
- **WHEN** the schema does not configure `apply.tracks`
|
||||
- **THEN** apply instructions report `taskTrackingConfigured: false`
|
||||
- **AND** the agent reports Task Completion as not applicable, not as skipped or failed
|
||||
- **AND** continues the checks that apply to the schema
|
||||
|
||||
### Requirement: Completeness Verification
|
||||
The agent SHALL verify that all required work has been completed.
|
||||
|
||||
#### Scenario: Task completion check
|
||||
- **WHEN** verifying completeness
|
||||
- **THEN** the agent reads tasks.md
|
||||
- **AND** counts tasks marked `- [x]` (complete) vs `- [ ]` (incomplete)
|
||||
- **THEN** the agent uses the top-level `tasks` and `progress` from apply instructions
|
||||
- **AND** apply instructions aggregate every concrete file matched by the active schema's `apply.tracks`, regardless of the tracked artifact's ID
|
||||
- **AND** reports complete and total task counts from `progress`
|
||||
- **AND** reports completion status with specific incomplete tasks listed
|
||||
- **AND** reports remaining checkboxes without descriptions when `progress.remaining` exceeds the listed incomplete tasks
|
||||
|
||||
#### Scenario: Tracking evidence becomes unavailable
|
||||
- **WHEN** one or more files matched by `apply.tracks` cannot be read after resolution
|
||||
- **THEN** apply instructions include every unavailable path and reason
|
||||
- **AND** preserve tasks and progress from readable tracking files
|
||||
- **AND** do not report `all_done`
|
||||
- **AND** the agent marks Task Completion as not verified from partial evidence
|
||||
|
||||
#### Scenario: Spec coverage check
|
||||
- **WHEN** verifying completeness
|
||||
- **AND** delta specs exist in `openspec/changes/<name>/specs/`
|
||||
- **THEN** the agent extracts all requirements from delta specs
|
||||
- **AND** searches codebase for implementation of each requirement
|
||||
- **AND** reports which requirements appear to have implementation vs which are missing
|
||||
- **THEN** the agent extracts all requirements from delta specs, noting the delta section each one sits under
|
||||
- **AND** searches codebase for implementation of each ADDED or MODIFIED requirement
|
||||
- **AND** reports which ADDED or MODIFIED requirements appear to have implementation vs which are missing
|
||||
- **AND** checks REMOVED and RENAMED requirements as described in the Removed requirement and Renamed requirement scenarios
|
||||
|
||||
#### Scenario: All tasks complete
|
||||
- **WHEN** all tasks are marked complete
|
||||
- **THEN** report "Tasks: N/N complete"
|
||||
- **AND** mark completeness dimension as passed
|
||||
- **AND** mark Task Completion as passed only when task descriptions are available
|
||||
- **AND** mark the completeness dimension as passed only when all applicable checks ran and passed
|
||||
|
||||
#### Scenario: Incomplete tasks found
|
||||
- **WHEN** some tasks are incomplete
|
||||
@@ -56,14 +73,14 @@ The agent SHALL verify that implementation matches the specifications.
|
||||
|
||||
#### Scenario: Requirement implementation mapping
|
||||
- **WHEN** verifying correctness
|
||||
- **THEN** for each requirement in delta specs:
|
||||
- **THEN** for each ADDED or MODIFIED requirement in delta specs:
|
||||
- Search codebase for implementation
|
||||
- Identify relevant files and line numbers
|
||||
- Assess whether implementation satisfies the requirement
|
||||
|
||||
#### Scenario: Scenario coverage check
|
||||
- **WHEN** verifying correctness
|
||||
- **THEN** for each scenario in delta specs:
|
||||
- **THEN** for each scenario under an ADDED or MODIFIED requirement in delta specs:
|
||||
- Check if the scenario's conditions are handled in code
|
||||
- Check if tests exist that cover the scenario
|
||||
- Report coverage status
|
||||
@@ -80,10 +97,33 @@ The agent SHALL verify that implementation matches the specifications.
|
||||
- **AND** suggest: either update implementation or update spec to match reality
|
||||
|
||||
#### Scenario: Missing implementation
|
||||
- **WHEN** no implementation found for a requirement
|
||||
- **WHEN** no implementation found for an ADDED or MODIFIED requirement
|
||||
- **THEN** report as CRITICAL issue
|
||||
- **AND** suggest: "Implement requirement X" with guidance on what's needed
|
||||
|
||||
#### Scenario: Removed requirement
|
||||
- **WHEN** a requirement sits under `## REMOVED Requirements` in a delta spec
|
||||
- **THEN** the agent treats the absence of its implementation as the expected result
|
||||
- **AND** does not report it as missing or suggest implementing it
|
||||
- **AND** reports it as CRITICAL only if the removed behavior is still present in the codebase
|
||||
- **AND** skips scenario coverage for it
|
||||
- **AND** does not treat matches in OpenSpec artifacts or docs, or in code that serves only the Migration note or an ADDED requirement, as evidence by themselves
|
||||
- **AND** still reports a code path that delivers the removed behavior, even when it is shared with an ADDED requirement
|
||||
|
||||
#### Scenario: Renamed requirement
|
||||
- **WHEN** a requirement is listed under `## RENAMED Requirements` in a delta spec
|
||||
- **THEN** the agent does not report its FROM name as missing
|
||||
- **AND** does not require code symbols or file names to be renamed
|
||||
- **AND** unless the TO name also appears under MODIFIED, verifies that the behavior of the baseline requirement (its body and scenarios in the main spec, under the FROM name, or under the TO name only when the main spec is already synced) is still implemented
|
||||
- **AND** reports CRITICAL "Renamed requirement not found" when that behavior is missing
|
||||
- **AND** marks spec coverage as not verified for the entry when the baseline requirement cannot be found or read
|
||||
|
||||
#### Scenario: Change that only removes or renames requirements
|
||||
- **WHEN** the delta specs are readable and contain at least one REMOVED or RENAMED requirement but no ADDED or MODIFIED requirements
|
||||
- **THEN** the agent reports requirement implementation mapping and scenario coverage as not applicable
|
||||
- **AND** does not mark them as not verified or withhold readiness because of them
|
||||
- **AND** a delta spec with no parseable requirements still marks them as not verified
|
||||
|
||||
### Requirement: Coherence Verification
|
||||
The agent SHALL verify that implementation is sensible and follows design decisions.
|
||||
|
||||
@@ -98,7 +138,7 @@ The agent SHALL verify that implementation is sensible and follows design decisi
|
||||
- **WHEN** verifying coherence
|
||||
- **AND** no design.md exists
|
||||
- **THEN** skip design adherence check
|
||||
- **AND** note "No design.md to verify against"
|
||||
- **AND** report "Design Adherence: Not verified (No design.md to verify against)"
|
||||
|
||||
#### Scenario: Design decision followed
|
||||
- **WHEN** implementation follows a design decision
|
||||
@@ -113,8 +153,10 @@ The agent SHALL verify that implementation is sensible and follows design decisi
|
||||
|
||||
#### Scenario: Code pattern consistency
|
||||
- **WHEN** verifying coherence
|
||||
- **AND** available artifacts support identifying implementation changes beyond a tasks-only check
|
||||
- **THEN** check if new code follows existing project patterns
|
||||
- **AND** flag any significant deviations as suggestions
|
||||
- **AND** report Code Pattern Consistency as not verified if implementation changes cannot be identified
|
||||
|
||||
### Requirement: Verification Report Format
|
||||
The agent SHALL produce a structured, prioritized report.
|
||||
@@ -132,6 +174,8 @@ The agent SHALL produce a structured, prioritized report.
|
||||
| Correctness | X/Y |
|
||||
| Coherence | Followed |
|
||||
```
|
||||
- **AND** report `Not verified (<reason>)` for every skipped or partially verified check in its dimension's status
|
||||
- **AND** never count a skipped check as passing
|
||||
|
||||
#### Scenario: Issue prioritization
|
||||
- **WHEN** issues are found
|
||||
@@ -147,7 +191,7 @@ The agent SHALL produce a structured, prioritized report.
|
||||
- **AND** avoid vague suggestions like "consider reviewing"
|
||||
|
||||
#### Scenario: All checks pass
|
||||
- **WHEN** no issues found across all dimensions
|
||||
- **WHEN** every applicable check ran and no issues were found across all dimensions
|
||||
- **THEN** display:
|
||||
```text
|
||||
All checks passed. Ready for archive.
|
||||
@@ -160,15 +204,31 @@ The agent SHALL produce a structured, prioritized report.
|
||||
X critical issue(s) found. Fix before archiving.
|
||||
```
|
||||
- **AND** do NOT suggest running archive
|
||||
- **AND** name every skipped check and its reason, if any
|
||||
|
||||
#### Scenario: Only warnings/suggestions
|
||||
- **WHEN** no CRITICAL issues but warnings exist
|
||||
#### Scenario: Only warnings
|
||||
- **WHEN** every applicable check ran and no CRITICAL issues but warnings exist
|
||||
- **THEN** display:
|
||||
```text
|
||||
No critical issues. Y warning(s) to consider.
|
||||
Ready for archive (with noted improvements).
|
||||
```
|
||||
|
||||
#### Scenario: Only suggestions
|
||||
- **WHEN** every applicable check ran and only suggestions exist
|
||||
- **THEN** report "No critical issues or warnings. Z suggestion(s) to consider. Ready for archive (with noted improvements)."
|
||||
|
||||
#### Scenario: Checks skipped
|
||||
- **WHEN** any check was skipped or partially verified and no CRITICAL issues exist
|
||||
- **THEN** report "No critical issues found in the checks that ran"
|
||||
- **AND** name every unverified check and its reason
|
||||
- **AND** include the warning count when nonzero
|
||||
- **AND** do not claim archive readiness
|
||||
|
||||
#### Scenario: Suggestions in final assessment
|
||||
- **WHEN** suggestions exist
|
||||
- **THEN** include their count in the final assessment, including assessments with critical issues or skipped checks
|
||||
|
||||
### Requirement: Flexible Artifact Handling
|
||||
The agent SHALL gracefully handle changes with varying artifact completeness.
|
||||
|
||||
@@ -188,3 +248,16 @@ The agent SHALL gracefully handle changes with varying artifact completeness.
|
||||
- **WHEN** change has proposal, design, specs, and tasks
|
||||
- **THEN** perform all verification checks
|
||||
- **AND** cross-reference artifacts for consistency
|
||||
|
||||
#### Scenario: Unusable or partial artifact evidence
|
||||
- **WHEN** an artifact cannot be read or lacks usable requirements, scenarios, or design decisions
|
||||
- **THEN** mark each affected check as not verified with its reason
|
||||
- **AND** continue checks supported by the remaining evidence without treating partial coverage as a fully verified check
|
||||
|
||||
#### Scenario: Intentional artifact omissions
|
||||
- **WHEN** a check has no supporting artifacts because the schema omits task tracking or optional artifacts, or the change declares `skip_specs: true`
|
||||
- **THEN** report the corresponding checks as not applicable and explain why
|
||||
- **AND** exclude not-applicable checks from skipped-check counts and readiness assessment
|
||||
- **AND** do not require or create optional or intentionally skipped artifacts to obtain a passing report
|
||||
- **AND** treat verification as advisory: not verified describes missing evidence for an applicable check, not a new archive gate
|
||||
- **AND** leave archive checks and user-confirmation behavior unchanged
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fission-ai/openspec",
|
||||
"version": "1.13.1",
|
||||
"version": "1.14.0",
|
||||
"description": "AI-native system for spec-driven development",
|
||||
"keywords": [
|
||||
"openspec",
|
||||
@@ -71,7 +71,7 @@
|
||||
"vitest": "^4.1.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^11.2.1",
|
||||
"@inquirer/core": "^12.0.0",
|
||||
"@inquirer/prompts": "^8.5.2",
|
||||
"chalk": "^5.6.2",
|
||||
"commander": "^14.0.0",
|
||||
|
||||
Generated
+272
-226
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ artifacts:
|
||||
- **Why**: 1-2 sentences on the problem or opportunity. What problem does this solve? Why now?
|
||||
- **What Changes**: Bullet list of changes. Be specific about new capabilities, modifications, or removals. Mark breaking changes with **BREAKING**.
|
||||
- **Capabilities**: Identify which specs will be created or modified:
|
||||
- **New Capabilities**: List capabilities being introduced. Each becomes a new `specs/<capability-path>/spec.md`. Use kebab-case for path segments you introduce (e.g., `user-auth` or `identity/user-auth`) and follow the project's existing spec organization.
|
||||
- **New Capabilities**: List capabilities being introduced. Each becomes a new `specs/<capability-path>/spec.md`. Name each capability for a durable system behavior (for example, `user-auth`), not the work in this change (for example, `add-login-endpoint`). Choose a cohesive boundary that can own related requirements as the system evolves; avoid broad catch-all capabilities. Use kebab-case for path segments you introduce (e.g., `user-auth` or `identity/user-auth`) and follow the project's existing spec organization.
|
||||
- **Modified Capabilities**: List existing capabilities whose REQUIREMENTS are changing. Only include if spec-level behavior changes (not just implementation details). Each needs a delta spec file. Use the exact existing path under `openspec/specs/`. Leave empty if no requirement changes.
|
||||
- **Impact**: Affected code, APIs, dependencies, or systems.
|
||||
|
||||
@@ -94,6 +94,7 @@ artifacts:
|
||||
- Each scenario: `#### Scenario: <name>` with WHEN/THEN format
|
||||
- **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently.
|
||||
- Every requirement MUST have at least one scenario.
|
||||
- Keep each requirement's description (the text between `### Requirement:` and its first scenario) to 500 characters or fewer. `openspec validate` flags longer descriptions in ADDED requirements and in the main spec. This is a warning: normal validation still passes, but `openspec validate --strict` fails on it. When writing a new requirement, state one behavior per requirement: move examples and edge cases into scenarios, and split a requirement that covers several behaviors into separate `### Requirement:` blocks, each with its own scenarios. Under MODIFIED, keep the existing requirement block whole; never split, trim or rewrite existing text just to meet the length. Split an existing long requirement only when the user asks for it, in a change made for that purpose: under MODIFIED, keep its header and every scenario and cut its description down to one behavior without changing its meaning, then add each behavior you removed as its own ADDED requirement with its own scenarios.
|
||||
|
||||
New capabilities only: the delta spec's first section is `## Purpose` -
|
||||
one or two sentences (50+ characters, or `openspec validate --strict`
|
||||
@@ -194,22 +195,34 @@ artifacts:
|
||||
would change what gets built, resolve them with the user first - do not
|
||||
bake an unstated assumption into the task list.
|
||||
|
||||
**IMPORTANT: Follow the template below exactly.** The apply phase parses
|
||||
**IMPORTANT: Follow the template below for tracked tasks.** The apply phase parses
|
||||
checkbox format to track progress. A box holding only `x` counts as done,
|
||||
upper or lower case and with any spacing, so `- [ x]` is done too. Every
|
||||
other marker, including `- [~]`, `- [-]` and an empty `- []`, reads as
|
||||
unfinished. A line with no checkbox is not tracked at all.
|
||||
|
||||
Guidelines:
|
||||
- Group related tasks under ## numbered headings
|
||||
- Each task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Group related tracked tasks under ## numbered headings
|
||||
- Each tracked task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Tasks should be small enough to complete in one session
|
||||
- Order tasks by dependency (what must be done first?)
|
||||
- Track implementation and verification work that can be completed before
|
||||
archive. If the requested workflow includes archive or work that requires
|
||||
this change to be archived, preserve those steps as plain bullets in an
|
||||
optional `## Workflow follow-up` section at the end of tasks.md. These
|
||||
bullets are reference information outside tracked task progress.
|
||||
- Each task MUST state how to verify completion (a test, command,
|
||||
observable behavior, or delivered artifact). Put the verification in
|
||||
that task's checkbox description. Use a separate verification task only
|
||||
when it checks broader integration or system behavior that spans
|
||||
multiple implementation tasks.
|
||||
- Each task group MUST land the tests and documentation its own work
|
||||
calls for. Do NOT collect testing or documentation into a final group -
|
||||
when a late group first exercises work from an early one, the failures
|
||||
cascade back through every group in between and force rework. A group
|
||||
whose work calls for neither, such as scaffolding or dependency setup,
|
||||
carries neither. A final group is for integration checks only, not for
|
||||
the tests and docs an earlier group owed.
|
||||
|
||||
Example:
|
||||
```
|
||||
@@ -224,6 +237,15 @@ artifacts:
|
||||
|
||||
- [ ] 2.1 Implement data export function and verify the export test passes
|
||||
- [ ] 2.2 Add CSV formatting utilities and verify unit tests cover quoting and delimiters
|
||||
- [ ] 2.3 Document the export API in docs/export.md and verify the documented command runs as written
|
||||
```
|
||||
|
||||
When applicable, append workflow follow-up as plain bullets, for example:
|
||||
```
|
||||
## Workflow follow-up
|
||||
|
||||
- Archive the change after the project's review requirements are satisfied.
|
||||
- Verify the archived result.
|
||||
```
|
||||
|
||||
Reference specs for what needs to be built, design for how to build it.
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
<!-- Capabilities being introduced. Use kebab-case for path segments you introduce
|
||||
(e.g., user-auth or identity/user-auth) that follow the project's existing
|
||||
spec organization. Each creates specs/<capability-path>/spec.md. -->
|
||||
<!-- Capabilities being introduced. Name each capability for a cohesive system
|
||||
behavior that can own related requirements as the system evolves. Do not name
|
||||
implementation tasks or proposal sections. Avoid broad catch-all names. Use
|
||||
kebab-case for path segments you introduce (e.g., user-auth or identity/user-auth)
|
||||
that follow the project's existing spec organization. Each creates
|
||||
specs/<capability-path>/spec.md. -->
|
||||
- `<capability-path>`: <brief description of what this capability covers>
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
@@ -10,18 +10,34 @@
|
||||
// `changeset publish` triggers `prepublishOnly` (also builds here). This
|
||||
// means an explicit build is not strictly necessary for the guard.
|
||||
|
||||
import { execFileSync } from 'child_process';
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import path from 'path';
|
||||
import spawn from 'cross-spawn';
|
||||
|
||||
function log(msg) {
|
||||
if (process.env.CI) return; // keep CI logs quiet by default
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
// cross-spawn, not execFileSync: on Windows `npm` is npm.cmd, which execFile
|
||||
// cannot resolve without a shell. Keeps the argv form, so no shell is involved.
|
||||
function run(cmd, args, opts = {}) {
|
||||
return execFileSync(cmd, args, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], ...opts });
|
||||
const result = spawn.sync(cmd, args, {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
...opts,
|
||||
});
|
||||
|
||||
if (result.error) throw result.error;
|
||||
if (result.status !== 0) {
|
||||
const stderr = (result.stderr || '').trim();
|
||||
throw new Error(
|
||||
`${cmd} ${args.join(' ')} exited with ${result.status}${stderr ? `: ${stderr}` : ''}`
|
||||
);
|
||||
}
|
||||
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
function npmPack() {
|
||||
|
||||
@@ -55,7 +55,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
This returns:
|
||||
- `contextFiles`: artifact ID -> array of concrete file paths (varies by schema - could be proposal/specs/design/tasks or spec/tests/implementation/docs)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Task list with status, source path, and source line
|
||||
- Dynamic instruction based on current state
|
||||
- Optional `context`: current required project instruction input from the selected root
|
||||
- Optional `operationGuidance`: current advisory guidance for apply
|
||||
@@ -65,7 +65,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- If `state: "blocked"`: show the message and pause implementation.
|
||||
- If `missingArtifacts` is non-empty: suggest using `/openspec-continue-change` to create them.
|
||||
- Otherwise, follow the CLI instruction to create or repair the schema-configured tracking file from existing planning artifacts. Do not assume another artifact is ready or start implementation while blocked.
|
||||
- If `state: "all_done"`: congratulate, suggest archive
|
||||
- If `state: "all_done"`: report that all tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
Treat `context` as a required prompt-level input. Read and consider it, and
|
||||
@@ -107,7 +107,9 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
||||
- Before editing, confirm the checkbox at the returned `sourcePath` and `line` still matches the task description; if it does not, rerun the apply instructions and use the refreshed location
|
||||
- Mark the task complete at its returned `sourcePath` and `line`: `- [ ]` → `- [x]`
|
||||
- Rerun the apply instructions and confirm that task is now done and progress changed
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
@@ -122,7 +124,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If all done: report that tracked tasks are complete and suggest review or verification as appropriate before archiving
|
||||
- If paused: explain why and wait for guidance
|
||||
|
||||
**Output During Implementation**
|
||||
@@ -153,7 +155,8 @@ Working on task 4/7: <task description>
|
||||
- [x] Task 2
|
||||
...
|
||||
|
||||
All tasks complete! You can archive this change with `/openspec-archive-change`.
|
||||
All tracked tasks are complete. Review or verify the change as appropriate
|
||||
before archiving. You can archive this change with `/openspec-archive-change`.
|
||||
```
|
||||
|
||||
**Output On Pause (Issue Encountered)**
|
||||
@@ -187,6 +190,7 @@ What would you like to do?
|
||||
- When a task needs work beyond what the spec describes, surface the added scope and pause - never silently narrow, defer, or simplify away specified behavior
|
||||
- Only mark a task `- [x]` when its specified behavior is fully implemented, not when it is partially done or deferred
|
||||
- Use contextFiles from CLI output, don't assume specific file names
|
||||
- Use each task's sourcePath and line to update its exact checkbox
|
||||
- Do not use context or operation guidance as proof that a task is complete
|
||||
- Apply relevant project context; report conflicts with controlling workflow inputs
|
||||
- Consider every guidance entry; explain any inapplicable or conflicting advice
|
||||
|
||||
@@ -85,20 +85,27 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
3. **Check task completion status**
|
||||
|
||||
Read the tasks file (typically `tasks.md`) to check for incomplete tasks.
|
||||
Run `openspec list --json` with the same selected-root flags and find the
|
||||
entry in `changes` whose `name` exactly matches the selected change.
|
||||
Require exactly one match and nonnegative integer `totalTasks` and
|
||||
`completedTasks`, with `completedTasks <= totalTasks`. The CLI resolves
|
||||
the schema's tracked task files, including custom artifact names, output
|
||||
paths, and globs.
|
||||
Incomplete tasks = `totalTasks - completedTasks`.
|
||||
|
||||
A checkbox is complete when its only content is `x` or `X`; spacing inside
|
||||
the brackets does not matter, so `- [ x]` counts as complete too. Every
|
||||
other marker is incomplete - `- [ ]`, an empty `- []`, and markers OpenSpec
|
||||
assigns no meaning to such as `- [~]` or `- [-]`. Never read an unfamiliar
|
||||
marker as complete.
|
||||
Do not infer task completion from artifact status or the absence of a
|
||||
top-level `tasks.md`. If the lookup fails, returns invalid JSON, omits or
|
||||
duplicates the selected change, or returns invalid counts, report the problem
|
||||
and stop before syncing or archiving.
|
||||
The CLI counts only `x`/`X` checkbox markers as complete;
|
||||
other markers, including unfamiliar ones, remain incomplete.
|
||||
|
||||
**If incomplete tasks found:**
|
||||
- Display warning showing count of incomplete tasks
|
||||
- Ask the user to confirm they want to proceed
|
||||
- Proceed if user confirms
|
||||
|
||||
**If no tasks file exists:** Proceed without task-related warning.
|
||||
**If `totalTasks` is zero:** Proceed without a task-related warning.
|
||||
|
||||
4. **Assess delta spec sync state**
|
||||
|
||||
@@ -139,10 +146,21 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
Then run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching `specs` instructions again. Do not delegate it to a background task — step 5 would move `changeRoot` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
|
||||
|
||||
If the sync reports any stop or blocking condition, treat the sync as failed.
|
||||
Stop the archive immediately. Do not perform the post-sync content comparison and do not move its `changeRoot`.
|
||||
Nothing has moved, so the user can fix the blocking condition or re-run the sync.
|
||||
|
||||
After the sync writes each main spec, verify its structure against the canonical sync contract:
|
||||
- A new main spec starts with a `# <capability> Specification` title. An existing main spec keeps its title exactly as it is.
|
||||
- Preserve existing `## Purpose` sections completely untouched for established main specs.
|
||||
- For a new main spec, copy the delta `## Purpose` verbatim. Warn only if the purpose text is shorter than standard validation expects. Do not regenerate or rewrite existing authored purpose. If no usable `## Purpose` is provided, use the existing TBD Purpose behavior and warning.
|
||||
- Verify that no delta-style section headers (`## ADDED Requirements`, `## MODIFIED Requirements`, `## REMOVED Requirements`, `## RENAMED Requirements`) remain in the main spec, adhering strictly to the sync workflow formatting rules.
|
||||
- Requirement blocks the sync wrote or changed use `### Requirement:` headings, and their scenarios use `#### Scenario:` headings, under the spec's `## Requirements` section. Leave content the delta does not mention exactly as it is.
|
||||
|
||||
Then re-run the comparison from the top of this step, including the explicitly retired, missing-spec case, against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty; a spec the sync deliberately kept and reported is also a match
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty.
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
|
||||
If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and `changeRoot` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
|
||||
|
||||
@@ -41,7 +41,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
2. **Prompt for change selection**
|
||||
|
||||
Ask the user to choose changes (multi-select):
|
||||
- Show each change with its schema
|
||||
- Show each change name and task status from the list output
|
||||
- Include an option for "All changes"
|
||||
- Allow any number of selections (1+ works, 2+ is the typical use case)
|
||||
|
||||
@@ -74,17 +74,24 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
3. **Batch validation - gather status for all selected changes**
|
||||
|
||||
Run `openspec list --json` once with the same selected-root flags for task
|
||||
progress. If the lookup fails, returns invalid JSON, or omits any selected
|
||||
change, contains a duplicate selected change, or returns invalid counts,
|
||||
report the problem and stop before syncing or archiving the batch.
|
||||
|
||||
For each selected change, collect:
|
||||
|
||||
a. **Artifact status** - Run `openspec status --change "<name>" --json`
|
||||
- Parse `schemaName`, `artifacts`, `planningHome`, `changeRoot`, `artifactPaths`, and `actionContext`
|
||||
- Note which artifacts are `done` vs other states
|
||||
|
||||
b. **Task completion** - Read `artifactPaths.tasks.existingOutputPaths` from status JSON
|
||||
- Complete means the checkbox holds only `x`/`X`, ignoring spacing
|
||||
(`- [ x]` is complete); every other marker is incomplete (`- [ ]`,
|
||||
`- []`, and unfamiliar ones such as `- [~]` or `- [-]`)
|
||||
- If no tasks file exists, note as "No tasks"
|
||||
b. **Task completion** - Find the `changes` entry from the list response whose `name` exactly matches this change
|
||||
- Require nonnegative integer `totalTasks` and `completedTasks`, with `completedTasks <= totalTasks`
|
||||
- Incomplete tasks = `totalTasks - completedTasks`
|
||||
- The CLI resolves the schema's tracked task files, including custom artifact names, output paths, and globs
|
||||
- Do not infer task completion from artifact status, an artifact id of `tasks`, or the absence of a top-level `tasks.md`
|
||||
- The CLI counts only `x`/`X` checkbox markers as complete; other markers remain incomplete
|
||||
- If `totalTasks` is zero, note as "No tasks"
|
||||
|
||||
c. **Delta specs** - Check `artifactPaths.specs.existingOutputPaths` from status JSON
|
||||
- List which capability specs exist
|
||||
@@ -199,6 +206,8 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
a. **Sync included delta specs**:
|
||||
- Run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) only for changes with entries in `includedDeltas`, passing only the included delta paths and explicitly instructing it to ignore that change's `excludedDeltas`. Wait for it to finish.
|
||||
- If the sync reports any stop or blocking condition, treat the sync as failed. Stop processing that change immediately. Before continuing to the next change, record this change's outcome as Failed in the batch results, including the sync blocking/error condition.
|
||||
- Do not perform the post-sync content comparison and do not move its `changeRoot`; leave the change intact.
|
||||
- For conflicts, apply in resolved order.
|
||||
- Pass that change's fetched specs-rule snapshot into inline sync; inline
|
||||
sync must reuse it without fetching instructions again
|
||||
@@ -213,7 +222,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Verify that main specs are updated:
|
||||
- ADDED requirements present
|
||||
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty; a spec the sync deliberately kept and reported is also a match
|
||||
- REMOVED requirements gone — and where this sync retired a capability (removed its last requirement, leaving `## Requirements` empty), its main spec deleted rather than left empty.
|
||||
- RENAMED requirements present under the new name and absent under the old one
|
||||
- Do not verify delta specs in `excludedDeltas`; they are intentionally left unsynced.
|
||||
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's `changeRoot` — do not archive that change. `changeRoot` remains intact.
|
||||
|
||||
@@ -37,7 +37,6 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
- How recently it was modified (from `lastModified` field)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ openspec list --json
|
||||
|
||||
This tells you:
|
||||
- If there are active changes
|
||||
- Their names, schemas, and status
|
||||
- Their names and task status
|
||||
- What the user might be working on
|
||||
|
||||
That is the *change* list - work in flight. It does not include the project's durable capabilities, so list those too:
|
||||
|
||||
@@ -91,7 +91,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Dependencies are enablers, not gates: if a required artifact is still `blocked` only because you skipped a conditional dependency, write it anyway
|
||||
- Stop when every artifact in the required set is `done`, `skipped`, or was deliberately skipped
|
||||
|
||||
c. **If an artifact requires user input** (unclear context):
|
||||
c. **If an artifact requires user input** (critically unclear context):
|
||||
- Ask the user to clarify
|
||||
- Then continue with creation
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ Save to the `resolvedOutputPath` from `openspec instructions design --change "<n
|
||||
|
||||
Finally, we break the work into implementation tasks—checkboxes that drive the apply phase.
|
||||
|
||||
These should be small, clear, and in logical order.
|
||||
These should be small, clear, and in logical order. Each group carries the tests and documentation for its own work - the last group is only for integration checks.
|
||||
```
|
||||
|
||||
**DO:** Generate tasks based on specs and design:
|
||||
@@ -401,12 +401,17 @@ Here are the implementation tasks:
|
||||
|
||||
---
|
||||
|
||||
Each checkbox becomes a unit of work in the apply phase. Ready to implement?
|
||||
Each checkbox becomes a unit of work in the apply phase. Does this task breakdown look right?
|
||||
```
|
||||
|
||||
**PAUSE** - Wait for user to confirm they're ready to implement.
|
||||
**PAUSE** - Wait for user approval/feedback.
|
||||
|
||||
Save to the `resolvedOutputPath` from `openspec instructions tasks --change "<name>" --json`.
|
||||
After approval, save to the `resolvedOutputPath` from `openspec instructions tasks --change "<name>" --json`.
|
||||
|
||||
Then ask:
|
||||
> "Tasks are saved. Ready to implement?"
|
||||
|
||||
**PAUSE** - Wait for user to confirm before implementation.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
|
||||
|
||||
When prompting, present the top 3-4 most recently modified changes as options, showing:
|
||||
- Change name
|
||||
- Schema (from `schema` field if present, otherwise "spec-driven")
|
||||
- Status (e.g., "0/5 tasks", "complete", "no tasks")
|
||||
- How recently it was modified (from `lastModified` field)
|
||||
|
||||
@@ -69,7 +68,13 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
|
||||
- Read the artifact(s) the request touches and the change's other existing artifacts.
|
||||
- Draft the requested edit in the conversation, not in files. Work out exactly what it changes; step 5 owns every write. Then check every other existing artifact against the drafted edit - in ANY direction: an edit to a later artifact may require revising an earlier one, not only the other way around. Build order is a useful reading order, not a constraint on which artifacts may be revised.
|
||||
- Note everything that is now inconsistent, missing, or contradictory.
|
||||
- Propose revisions only to files that already exist (`existingOutputPaths`). Do NOT create artifacts that don't exist yet, and do NOT invent new files under a glob artifact - note them and point the user to `/openspec-continue-change` to create them.
|
||||
- Propose revisions to files that already exist (`existingOutputPaths`). If an artifact has no existing output files and status `ready` or `blocked`, note it and point the user to `/openspec-continue-change` to create them. Leave `skipped` artifacts untouched; do not treat them as missing or defer them to the continue workflow.
|
||||
- A glob artifact (e.g. `specs/**/*.md`) is marked `done` after at least one file matches, and the continue workflow only handles `ready` artifacts. When reconciliation identifies a missing file for a glob artifact whose `existingOutputPaths` is non-empty:
|
||||
1. Run `openspec instructions "<artifact-id>" --change "<name>" --json` and use its `instruction` and `template`. Treat `context` and `rules` as constraints; do not copy them into the file. If instructions report `skipped: true`, do not create the file. Read current dependency files from disk; if a required non-skipped dependency is missing, stop and ask the user to restore it first.
|
||||
2. Choose a concrete path inside `changeRoot` that matches `artifactPaths.<id>.outputPath` and does not already exist. Verify it remains inside `changeRoot` after resolving any symlinked parent directories. The glob `resolvedOutputPath` is not a valid target.
|
||||
3. Include the new file in step 5's proposed revisions and create it only after the user confirms.
|
||||
4. After confirmation, immediately before creation, refresh status and instructions. Verify the artifact is still in scope, not skipped, and partially populated; repeat the concrete-path checks above.
|
||||
5. Use a create operation that fails if the target already exists. If `instruction` delegates creation to another skill or command, invoke it only if it can honor the confirmed path and these guardrails; otherwise stop. If any check fails or the confirmed draft is no longer valid, stop and reconcile with the user rather than replacing existing content or choosing a different path.
|
||||
- If the change is already coherent, say so and propose no revisions.
|
||||
|
||||
5. **Confirm and apply, one artifact at a time**
|
||||
@@ -82,7 +87,7 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
|
||||
```
|
||||
|
||||
6. **Point to the next step (guidance only - NEVER act on it)**
|
||||
- Artifacts still missing -> suggest `/openspec-continue-change` to create them.
|
||||
- Artifacts with empty `existingOutputPaths` and status `ready` or `blocked` -> suggest `/openspec-continue-change` to create them.
|
||||
- Change already implemented (tasks checked off / already applied) -> the code may no longer match the revised plan; suggest `/openspec-apply-change` to carry the delta into code.
|
||||
- Everything done and implemented -> suggest `/openspec-archive-change`.
|
||||
|
||||
@@ -90,13 +95,14 @@ This workflow revises artifacts that already exist; `/openspec-continue-change`
|
||||
|
||||
After each invocation, show:
|
||||
- Which artifacts were revised (and which proposed revisions were rejected)
|
||||
- Anything deferred to `/openspec-continue-change` (not-yet-created artifacts or files)
|
||||
- Any file created under a glob artifact that was already partially populated
|
||||
- Anything deferred to `/openspec-continue-change` (artifacts with no files yet and status `ready` or `blocked`, never `skipped` artifacts)
|
||||
- Where the change stands and the recommended next command
|
||||
|
||||
**Guardrails**
|
||||
- Planning artifacts only - NEVER edit implementation code. If the revised plan implies code changes, stop and point to `/openspec-apply-change`.
|
||||
- Use the artifact ids and paths reported by `openspec status`; never branch on hardcoded artifact names.
|
||||
- Edit only the concrete files in `existingOutputPaths`; never write to a glob `resolvedOutputPath`.
|
||||
- Do not advance the build frontier: no new artifacts, no new files under glob artifacts - that is `/openspec-continue-change`'s job.
|
||||
- Do not advance the build frontier: if an artifact has empty `existingOutputPaths` and status `ready` or `blocked`, that is `/openspec-continue-change`'s job. Leave `skipped` artifacts untouched. The only new-file scope is a confirmed concrete path under a glob artifact whose `existingOutputPaths` is non-empty.
|
||||
- Confirm every edit with the user before writing.
|
||||
- If the request changes the change's *intent* rather than refining it, recommend starting fresh with `/openspec-new-change` (the "Update vs. Start Fresh" heuristic).
|
||||
|
||||
@@ -35,7 +35,7 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Auto-select if only one active change exists
|
||||
- If ambiguous, run `openspec list --json` to get available changes and ask the user to select one
|
||||
|
||||
When prompting, show changes that have implementation tasks (tasks artifact exists).
|
||||
When prompting, show all active changes returned by the list, including changes with `status: "no-tasks"`.
|
||||
Include the schema used for each change if available.
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
@@ -56,7 +56,9 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns the change directory and `contextFiles` (artifact ID -> array of concrete file paths). Read all available artifacts from `contextFiles`.
|
||||
This returns the change directory, `contextFiles` (artifact ID -> array of concrete file paths), `taskTrackingConfigured`, and top-level `tasks` and `progress` aggregated from every concrete file matched by the schema's `apply.tracks` configuration that could be read. Read all available artifacts from `contextFiles`.
|
||||
|
||||
Treat apply `state` and `instruction` as context, not a verification verdict. Do not implement tasks or archive the change during verification.
|
||||
|
||||
4. **Initialize verification report structure**
|
||||
|
||||
@@ -67,32 +69,59 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
|
||||
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
||||
|
||||
Verification is advisory. Respect intentional omissions such as `skip_specs: true`, optional design documents, and schemas without task tracking. Do not require or invent optional or intentionally omitted artifacts to obtain a clean report. `Not verified` describes a limit of this report, not a new archive prerequisite. Archive retains its own checks and user-confirmation behavior.
|
||||
|
||||
Mark checks the schema does not define, or artifacts the status reports as intentionally skipped, as **Not applicable**. The correctness checks of a change whose readable delta specs contain REMOVED or RENAMED requirements but no ADDED or MODIFIED requirements are also **Not applicable** (see step 6). Exclude them from skipped-check counts and the archive-readiness assessment. Reserve **Not verified** for applicable checks whose evidence is missing or unusable.
|
||||
|
||||
If only task evidence is available for applicable checks, verify task completion only and mark the remaining applicable checks, including **Code Pattern Consistency**, as not verified with the reason "Only task evidence available".
|
||||
|
||||
If artifacts cannot be read or contain no usable requirements, scenarios, or design decisions, mark the affected checks as not verified with the specific reason. Continue checks supported by the remaining evidence, but a partially checked input set is not a fully verified check. Missing requirements affect Spec Coverage and Requirement Implementation Mapping; missing scenarios affect Scenario Coverage; missing design decisions affect Design Adherence.
|
||||
|
||||
5. **Verify Completeness**
|
||||
|
||||
**Task Completion**:
|
||||
- If `contextFiles.tasks` exists, read every file path in it
|
||||
- Parse checkboxes: complete means the box holds only `x`/`X`, ignoring
|
||||
spacing (`- [ x]` is complete); every other marker is incomplete
|
||||
(`- [ ]`, `- []`, and unfamiliar ones such as `- [~]` or `- [-]`)
|
||||
- Count complete vs total tasks
|
||||
- If incomplete tasks exist:
|
||||
- Add CRITICAL issue for each incomplete task
|
||||
- If `taskTrackingConfigured` is false, report **Task Completion** as not applicable. Do not treat empty `tasks` as missing evidence.
|
||||
- Otherwise, use the top-level `tasks` and `progress` fields. They already aggregate every readable concrete file matched by `apply.tracks`, regardless of the tracked artifact's ID; do not infer tracking from a `contextFiles` key.
|
||||
- If `unavailableTrackingFiles` is nonempty, mark **Task Completion** as not verified and include every unavailable path and reason. Continue using any readable task evidence, but do not infer completion from the partial `tasks` and `progress` fields.
|
||||
- If `taskTrackingConfigured` is true and `tasks` is empty, mark **Task Completion** as not verified and record the reason from apply `state` and `instruction`. Nonzero totals alone do not establish evaluable task descriptions.
|
||||
- Report complete vs total tasks from `progress`.
|
||||
- If `progress.remaining` is greater than 0:
|
||||
- Add CRITICAL issue for each listed incomplete task. If the remaining count exceeds the listed incomplete tasks, also report the incomplete checkboxes without descriptions and recommend adding descriptions and completing them. Do not infer completion from the listed tasks alone.
|
||||
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
||||
|
||||
**Spec Coverage**:
|
||||
- If status marks the spec artifact skipped by `skip_specs: true`, or the schema defines no spec artifact, report the spec-dependent checks as not applicable.
|
||||
- Otherwise, `contextFiles` is keyed by artifact id, and artifact ids come from the active schema. If `contextFiles.specs` is absent or empty, mark **Spec Coverage**, **Requirement Implementation Mapping**, and **Scenario Coverage** as not verified; do not treat any of them as clean.
|
||||
- If delta specs exist in `contextFiles.specs`:
|
||||
- Extract all requirements (marked with "### Requirement:")
|
||||
- For each requirement:
|
||||
- Extract all requirements (marked with "### Requirement:", or listed as `FROM:`/`TO:` pairs under `## RENAMED Requirements`) and note the delta section each one sits under: `## ADDED`, `## MODIFIED`, `## REMOVED`, or `## RENAMED Requirements`. The section decides what the check looks for.
|
||||
- For each ADDED or MODIFIED requirement (for MODIFIED, check the text in the delta, not the old wording):
|
||||
- Search codebase for keywords related to the requirement
|
||||
- Assess if implementation likely exists
|
||||
- If requirements appear unimplemented:
|
||||
- If ADDED or MODIFIED requirements appear unimplemented:
|
||||
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
||||
- Recommendation: "Implement requirement X: <description>"
|
||||
- For each REMOVED requirement, the change asks for the behavior to be gone, so invert the check:
|
||||
- Search codebase for the removed behavior. Matches in `openspec/` artifacts or docs, or in code that serves only the Migration note or an ADDED requirement, are not evidence by themselves. Report any code path that still delivers the removed behavior, including one shared with an ADDED requirement.
|
||||
- Finding no implementation is the expected result. Never report a REMOVED requirement as "Requirement not found" or recommend implementing it.
|
||||
- If the behavior is still present:
|
||||
- Add CRITICAL issue: "Removed requirement still implemented: <requirement name>"
|
||||
- Recommendation: "Remove the remaining implementation at <file>:<lines>, following the requirement's Migration note if it has one"
|
||||
- For each RENAMED entry (`FROM:`/`TO:`), the name changes but the behavior stays, so check the TO requirement for that unchanged behavior:
|
||||
- Do not report the FROM name as missing, and do not require code symbols, identifiers, or file names to be renamed.
|
||||
- If the TO name also appears under MODIFIED, its behavior is checked there against the MODIFIED text; skip it here.
|
||||
- Otherwise, read the baseline requirement in the main spec at `<planningHome.root>/openspec/specs/<capability-path>/spec.md`, using the same capability path as the delta spec: the requirement under the FROM name, or under the TO name only when the FROM name is absent because the main spec is already synced. Its body and scenarios are the evidence for the behavior the TO requirement keeps.
|
||||
- Search codebase for that behavior and assess if it is still implemented.
|
||||
- If it appears unimplemented:
|
||||
- Add CRITICAL issue: "Renamed requirement not found: <TO name>"
|
||||
- Recommendation: "Restore the behavior of <TO name> (renamed from <FROM name>); a rename must not change behavior"
|
||||
- If the baseline requirement cannot be found or read, mark **Spec Coverage** as not verified for that entry with the reason. Never count an unchecked rename as passing.
|
||||
|
||||
6. **Verify Correctness**
|
||||
|
||||
If the delta specs are readable and contain at least one REMOVED or RENAMED requirement but no ADDED or MODIFIED requirements (the change only removes or renames requirements), report **Requirement Implementation Mapping** and **Scenario Coverage** as **Not applicable**. The REMOVED and RENAMED checks under Spec Coverage are the evidence for such a change (each RENAMED entry is checked there against its baseline behavior), so do not mark these two checks as not verified. A delta spec with no parseable requirements at all is unusable evidence, not a removal-only change: mark these checks as not verified.
|
||||
|
||||
**Requirement Implementation Mapping**:
|
||||
- For each requirement from delta specs:
|
||||
- For each ADDED or MODIFIED requirement from delta specs (REMOVED entries, and RENAMED entries without a MODIFIED block, were settled under Spec Coverage):
|
||||
- Search codebase for implementation evidence
|
||||
- If found, note file paths and line ranges
|
||||
- Assess if implementation matches requirement intent
|
||||
@@ -101,26 +130,29 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Recommendation: "Review <file>:<lines> against requirement X"
|
||||
|
||||
**Scenario Coverage**:
|
||||
- For each scenario in delta specs (marked with "#### Scenario:"):
|
||||
- For each scenario under an ADDED or MODIFIED requirement in delta specs (marked with "#### Scenario:"):
|
||||
- Check if conditions are handled in code
|
||||
- Check if tests exist covering the scenario
|
||||
- If scenario appears uncovered:
|
||||
- Add WARNING: "Scenario not covered: <scenario name>"
|
||||
- Recommendation: "Add test or implementation for scenario: <description>"
|
||||
- Skip scenarios under a REMOVED requirement; that behavior is meant to be gone.
|
||||
|
||||
7. **Verify Coherence**
|
||||
|
||||
**Design Adherence**:
|
||||
- If the schema defines no design artifact, report **Design Adherence** as not applicable.
|
||||
- If `contextFiles.design` exists:
|
||||
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
||||
- Verify implementation follows those decisions
|
||||
- If contradiction detected:
|
||||
- Add WARNING: "Design decision not followed: <decision>"
|
||||
- Recommendation: "Update implementation or revise design.md to match reality"
|
||||
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
||||
- Otherwise, if `contextFiles.design` is absent or empty: mark **Design Adherence** as not verified. With other supporting artifacts, **Code Pattern Consistency** still runs; the task-only case remains limited to task completion.
|
||||
|
||||
**Code Pattern Consistency**:
|
||||
- Review new code for consistency with project patterns
|
||||
- If implementation changes cannot be identified, mark **Code Pattern Consistency** as not verified and explain the missing evidence.
|
||||
- Otherwise, review new code for consistency with project patterns
|
||||
- Check file naming, directory structure, coding style
|
||||
- If significant deviations found:
|
||||
- Add SUGGESTION: "Code pattern deviation: <details>"
|
||||
@@ -140,11 +172,15 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
| Coherence | Followed/Issues |
|
||||
```
|
||||
|
||||
In each Status cell, report the results of checks that ran and `Not verified (<reason>)` for every skipped check. If all checks in a dimension were skipped, start the cell with `Not verified`. Never score a skipped check as passing. Treat every not verified or partially verified check as skipped in the final assessment. Count only ADDED and MODIFIED requirements in N, and report REMOVED and RENAMED requirements separately (for example, "1 removal confirmed, 1 rename verified"). For a change that only removes or renames requirements, the Correctness cell reads `Not applicable (no ADDED or MODIFIED requirements)`.
|
||||
|
||||
**Issues by Priority**:
|
||||
|
||||
1. **CRITICAL** (Must fix before archive):
|
||||
- Incomplete tasks
|
||||
- Missing requirement implementations
|
||||
- Removed requirements still implemented
|
||||
- Renamed requirements whose behavior is no longer implemented
|
||||
- Each with specific, actionable recommendation
|
||||
|
||||
2. **WARNING** (Should fix):
|
||||
@@ -158,9 +194,12 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- Each with specific recommendation
|
||||
|
||||
**Final Assessment**:
|
||||
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
||||
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
||||
- If all clear: "All checks passed. Ready for archive."
|
||||
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving." If any check was skipped, also name every skipped check and its reason.
|
||||
- If no CRITICAL issues, one or more warnings, and no checks were skipped: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
||||
- If only suggestions and no checks were skipped: "No critical issues or warnings. Z suggestion(s) to consider. Ready for archive (with noted improvements)."
|
||||
- If no issues and no checks were skipped: "All checks passed. Ready for archive."
|
||||
- If any check was skipped and there are no CRITICAL issues: do not claim readiness. Say "No critical issues found in the checks that ran. <check(s)> not verified: <reason>." Include the warning count when nonzero.
|
||||
- Include the suggestion count when nonzero in every final assessment.
|
||||
|
||||
**Verification Heuristics**
|
||||
|
||||
@@ -170,13 +209,6 @@ In both branches, never create the root as a side effect: do not run `openspec i
|
||||
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
||||
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
||||
|
||||
**Graceful Degradation**
|
||||
|
||||
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
||||
- If tasks + specs exist: verify completeness and correctness, skip design
|
||||
- If full artifacts: verify all three dimensions
|
||||
- Always note which checks were skipped and why
|
||||
|
||||
**Output Format**
|
||||
|
||||
Use clear markdown with:
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Command } from 'commander';
|
||||
|
||||
/**
|
||||
* Register the config command and all its subcommands.
|
||||
*
|
||||
* @param program - The Commander program instance
|
||||
*/
|
||||
export function registerConfigCommand(program: Command): void {
|
||||
const configCmd = program
|
||||
.command('config')
|
||||
.description('View and modify global OpenSpec configuration')
|
||||
.option('--scope <scope>', 'Config scope (only "global" supported currently)')
|
||||
.hook('preAction', (thisCommand) => {
|
||||
const opts = thisCommand.opts();
|
||||
if (opts.scope && opts.scope !== 'global') {
|
||||
console.error('Error: Project-local config is not yet implemented');
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// config path
|
||||
configCmd
|
||||
.command('path')
|
||||
.description('Show config file location')
|
||||
.action(async () => {
|
||||
const { configPathCommand } = await import('../../commands/config.js');
|
||||
configPathCommand();
|
||||
});
|
||||
|
||||
// config list
|
||||
configCmd
|
||||
.command('list')
|
||||
.description('Show all current settings')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options: { json?: boolean }) => {
|
||||
const { configListCommand } = await import('../../commands/config.js');
|
||||
configListCommand(options);
|
||||
});
|
||||
|
||||
// config get
|
||||
configCmd
|
||||
.command('get <key>')
|
||||
.description('Get a specific value (raw, scriptable)')
|
||||
.action(async (key: string) => {
|
||||
const { configGetCommand } = await import('../../commands/config.js');
|
||||
configGetCommand(key);
|
||||
});
|
||||
|
||||
// config set
|
||||
configCmd
|
||||
.command('set <key> <value>')
|
||||
.description('Set a value (auto-coerce types)')
|
||||
.option('--string', 'Force value to be stored as string')
|
||||
.option('--allow-unknown', 'Allow setting unknown keys')
|
||||
.action(async (key: string, value: string, options: { string?: boolean; allowUnknown?: boolean }) => {
|
||||
const { configSetCommand } = await import('../../commands/config.js');
|
||||
configSetCommand(key, value, options);
|
||||
});
|
||||
|
||||
// config unset
|
||||
configCmd
|
||||
.command('unset <key>')
|
||||
.description('Remove a key (revert to default)')
|
||||
.action(async (key: string) => {
|
||||
const { configUnsetCommand } = await import('../../commands/config.js');
|
||||
configUnsetCommand(key);
|
||||
});
|
||||
|
||||
// config reset
|
||||
configCmd
|
||||
.command('reset')
|
||||
.description('Reset configuration to defaults')
|
||||
.option('--all', 'Reset all configuration (required)')
|
||||
.option('-y, --yes', 'Skip confirmation prompts')
|
||||
.action(async (options: { all?: boolean; yes?: boolean }) => {
|
||||
const { configResetCommand } = await import('../../commands/config.js');
|
||||
await configResetCommand(options);
|
||||
});
|
||||
|
||||
// config edit
|
||||
configCmd
|
||||
.command('edit')
|
||||
.description('Open config in $EDITOR')
|
||||
.action(async () => {
|
||||
const { configEditCommand } = await import('../../commands/config.js');
|
||||
await configEditCommand();
|
||||
});
|
||||
|
||||
// config profile [preset]
|
||||
configCmd
|
||||
.command('profile [preset]')
|
||||
.description('Configure workflow profile (interactive picker or preset shortcut)')
|
||||
.action(async (preset?: string) => {
|
||||
const { configProfileCommand } = await import('../../commands/config.js');
|
||||
await configProfileCommand(preset);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Command, Option } from 'commander';
|
||||
import { COMMAND_REGISTRY } from '../../core/completions/command-registry.js';
|
||||
import { COMMON_FLAGS } from '../../core/completions/shared-flags.js';
|
||||
import type { ContextOptions } from '../../commands/context.js';
|
||||
|
||||
export function registerContextCommand(program: Command): void {
|
||||
const description =
|
||||
COMMAND_REGISTRY.find((entry) => entry.name === 'context')?.description ??
|
||||
'Print the working context for the resolved OpenSpec root';
|
||||
|
||||
program
|
||||
.command('context')
|
||||
.description(description)
|
||||
.option('--store <id>', COMMON_FLAGS.store.description)
|
||||
.addOption(
|
||||
new Option('--store-path <path>', 'Removed; register the store and use --store').hideHelp()
|
||||
)
|
||||
.option('--json', 'Output the agent brief as JSON')
|
||||
.option('--code-workspace <path>', 'Also write a VS Code workspace file for the set')
|
||||
.option('--force', 'Overwrite an existing --code-workspace file')
|
||||
.action(async (options: ContextOptions) => {
|
||||
const { contextCommand } = await import('../../commands/context.js');
|
||||
await contextCommand(options);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Command, Option } from 'commander';
|
||||
import { COMMAND_REGISTRY } from '../../core/completions/command-registry.js';
|
||||
import { COMMON_FLAGS } from '../../core/completions/shared-flags.js';
|
||||
import type { DoctorOptions } from '../../commands/doctor.js';
|
||||
|
||||
export function registerDoctorCommand(program: Command): void {
|
||||
const description =
|
||||
COMMAND_REGISTRY.find((entry) => entry.name === 'doctor')?.description ??
|
||||
'Report relationship health for the resolved OpenSpec root';
|
||||
|
||||
program
|
||||
.command('doctor')
|
||||
.description(description)
|
||||
.option('--store <id>', COMMON_FLAGS.store.description)
|
||||
.addOption(
|
||||
new Option('--store-path <path>', 'Removed; register the store and use --store').hideHelp()
|
||||
)
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options: DoctorOptions) => {
|
||||
const { doctorCommand } = await import('../../commands/doctor.js');
|
||||
await doctorCommand(options);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Command } from 'commander';
|
||||
|
||||
/**
|
||||
* Register the schema command and all its subcommands.
|
||||
*/
|
||||
export function registerSchemaCommand(program: Command): void {
|
||||
const schemaCmd = program
|
||||
.command('schema')
|
||||
.description('Manage workflow schemas [experimental]');
|
||||
|
||||
// Experimental warning
|
||||
schemaCmd.hook('preAction', () => {
|
||||
console.error('Note: Schema commands are experimental and may change.');
|
||||
});
|
||||
|
||||
// schema which
|
||||
schemaCmd
|
||||
.command('which [name]')
|
||||
.description('Show where a schema resolves from')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--all', 'List all schemas with their resolution sources')
|
||||
.action(async (name?: string, options?: { json?: boolean; all?: boolean }) => {
|
||||
const { schemaWhichCommand } = await import('../../commands/schema.js');
|
||||
await schemaWhichCommand(name, options);
|
||||
});
|
||||
|
||||
// schema validate
|
||||
schemaCmd
|
||||
.command('validate [name]')
|
||||
.description('Validate a schema structure and templates')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--verbose', 'Show detailed validation steps')
|
||||
.action(async (name?: string, options?: { json?: boolean; verbose?: boolean }) => {
|
||||
const { schemaValidateCommand } = await import('../../commands/schema.js');
|
||||
await schemaValidateCommand(name, options);
|
||||
});
|
||||
|
||||
// schema fork
|
||||
schemaCmd
|
||||
.command('fork <source> [name]')
|
||||
.description('Copy an existing schema to project for customization')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--force', 'Overwrite existing destination')
|
||||
.action(async (source: string, name?: string, options?: { json?: boolean; force?: boolean }) => {
|
||||
const { schemaForkCommand } = await import('../../commands/schema.js');
|
||||
await schemaForkCommand(source, name, options);
|
||||
});
|
||||
|
||||
// schema init
|
||||
schemaCmd
|
||||
.command('init <name>')
|
||||
.description('Create a new project-local schema')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--description <text>', 'Schema description')
|
||||
.option('--artifacts <list>', 'Comma-separated artifact IDs (proposal,specs,design,tasks)')
|
||||
.option('--default', 'Set as project default schema')
|
||||
.option('--no-default', 'Do not prompt to set as default')
|
||||
.option('--force', 'Overwrite existing schema')
|
||||
.action(async (
|
||||
name: string,
|
||||
options?: {
|
||||
json?: boolean;
|
||||
description?: string;
|
||||
artifacts?: string;
|
||||
default?: boolean;
|
||||
force?: boolean;
|
||||
}
|
||||
) => {
|
||||
const { schemaInitCommand } = await import('../../commands/schema.js');
|
||||
await schemaInitCommand(name, options);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Command } from 'commander';
|
||||
import type { ShowOptions } from '../../commands/spec.js';
|
||||
|
||||
export function registerSpecCommand(rootProgram: Command) {
|
||||
const specCommand = rootProgram
|
||||
.command('spec')
|
||||
.description('Manage and view OpenSpec specifications');
|
||||
|
||||
// Deprecation notice for noun-based commands
|
||||
specCommand.hook('preAction', () => {
|
||||
console.error('Warning: The "openspec spec ..." commands are deprecated. Prefer verb-first commands (e.g., "openspec show", "openspec validate --specs").');
|
||||
});
|
||||
|
||||
specCommand
|
||||
.command('show [spec-id]')
|
||||
.description('Display a specific specification')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
|
||||
.option('--no-scenarios', 'JSON only: Exclude scenario content')
|
||||
.option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
|
||||
.option('--no-interactive', 'Disable interactive prompts')
|
||||
.action(async (specId: string | undefined, options: ShowOptions & { noInteractive?: boolean }) => {
|
||||
const { specShowCommand } = await import('../../commands/spec.js');
|
||||
await specShowCommand(specId, options);
|
||||
});
|
||||
|
||||
specCommand
|
||||
.command('list')
|
||||
.description('List all available specifications')
|
||||
.option('--json', 'Output as JSON')
|
||||
.option('--long', 'Show id and title with counts')
|
||||
.action(async (options: { json?: boolean; long?: boolean }) => {
|
||||
const { specListCommand } = await import('../../commands/spec.js');
|
||||
await specListCommand(options);
|
||||
});
|
||||
|
||||
specCommand
|
||||
.command('validate [spec-id]')
|
||||
.description('Validate a specification structure')
|
||||
.option('--strict', 'Enable strict validation mode')
|
||||
.option('--json', 'Output validation report as JSON')
|
||||
.option('--no-interactive', 'Disable interactive prompts')
|
||||
.action(async (specId: string | undefined, options: { strict?: boolean; json?: boolean; noInteractive?: boolean }) => {
|
||||
const { specValidateCommand } = await import('../../commands/spec.js');
|
||||
await specValidateCommand(specId, options);
|
||||
});
|
||||
|
||||
return specCommand;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user