mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-04 06:18:24 +08:00
Compare commits
93
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93e27a755c | ||
|
|
8e9e457c05 | ||
|
|
296ecbc20a | ||
|
|
871dece1be | ||
|
|
8886e3ae22 | ||
|
|
3f0ca3f6ce | ||
|
|
8ac624b279 | ||
|
|
4ef0761080 | ||
|
|
7e21cc59ef | ||
|
|
a5bfedafc8 | ||
|
|
9a0dfb5cd1 | ||
|
|
a70daccf0e | ||
|
|
5956a8e872 | ||
|
|
65a7233f36 | ||
|
|
a3253051ea | ||
|
|
546224e00d | ||
|
|
96f6cacb20 | ||
|
|
737518b36f | ||
|
|
f987cf3e29 | ||
|
|
cbf386bd68 | ||
|
|
bb1f18c483 | ||
|
|
41ceebe2d8 | ||
|
|
a0decbe3fa | ||
|
|
1b06fddd59 | ||
|
|
0a01146c18 | ||
|
|
bc7ab26650 | ||
|
|
aa16080d16 | ||
|
|
055957fbca | ||
|
|
9e78bcaa80 | ||
|
|
e36463074d | ||
|
|
9aded17af7 | ||
|
|
0c5f0c6c48 | ||
|
|
21c1805d80 | ||
|
|
11b2690618 | ||
|
|
fd92ccca74 | ||
|
|
e441287b1f | ||
|
|
7fdb177158 | ||
|
|
79303b5210 | ||
|
|
8498042fe8 | ||
|
|
053d8a59d5 | ||
|
|
b642398bf3 | ||
|
|
ff506c347a | ||
|
|
1cdf0410df | ||
|
|
d5c824d4cd | ||
|
|
849ae2a976 | ||
|
|
f510581b6c | ||
|
|
7c3acccaf7 | ||
|
|
435458be56 | ||
|
|
76c80f80f3 | ||
|
|
0ca74762dc | ||
|
|
e6d81ba0f6 | ||
|
|
2d189ce5e0 | ||
|
|
44e4beeee8 | ||
|
|
a974c67986 | ||
|
|
485c97e97d | ||
|
|
347f0277e3 | ||
|
|
cb9641a450 | ||
|
|
342ed43e69 | ||
|
|
3c7a05c5dc | ||
|
|
d1f3861d9e | ||
|
|
7a39e887bb | ||
|
|
18c445a48d | ||
|
|
900174000b | ||
|
|
f529b25968 | ||
|
|
93f7b797cf | ||
|
|
7d07101363 | ||
|
|
c0f29044f9 | ||
|
|
7fe45ca330 | ||
|
|
c8e2072e3a | ||
|
|
cd5e49346f | ||
|
|
a18d992fa1 | ||
|
|
4df6a4889b | ||
|
|
9b5007dbc3 | ||
|
|
cce787ec40 | ||
|
|
94d651de8c | ||
|
|
040e382d64 | ||
|
|
caafd7c9bf | ||
|
|
144528257d | ||
|
|
af0b3418d0 | ||
|
|
7fd5417ed0 | ||
|
|
5ac1e12b83 | ||
|
|
fd7ad273c7 | ||
|
|
ea6f380fea | ||
|
|
765df47ad3 | ||
|
|
64d476f8b9 | ||
|
|
afdca0d5da | ||
|
|
61eb999f7c | ||
|
|
3d3bf96061 | ||
|
|
d199dfa407 | ||
|
|
d7d186088e | ||
|
|
6a3a1263fe | ||
|
|
1e94443a35 | ||
|
|
a0608d0bab |
+13
-11
@@ -12,11 +12,12 @@ Follow the prompts to select version bump type and describe your changes.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Add a changeset** — Run `pnpm changeset` locally before or after your PR
|
||||
2. **Version PR** — CI opens/updates a "Version Packages" PR when changesets merge to main
|
||||
3. **Release** — Merging the Version PR triggers npm publish and GitHub Release
|
||||
1. **Choose the release path**: Maintainers decide whether a PR follows the normal release cadence or gets dedicated release tracking.
|
||||
2. **Add dedicated release tracking**: When a maintainer asks for a changeset, run `pnpm changeset` locally before or after your PR.
|
||||
3. **Version PR**: CI opens/updates a "Version Packages" PR when changesets merge to main.
|
||||
4. **Release**: Merging the Version PR triggers npm publish and GitHub Release.
|
||||
|
||||
> **Note:** Contributors only need to run `pnpm changeset`. Versioning (`changeset version`) and publishing happen automatically in CI.
|
||||
> **Note:** The default path is the normal release cadence. Add a changeset when a maintainer or release owner wants dedicated release notes and version tracking for the PR. Versioning (`changeset version`) and publishing happen automatically in CI.
|
||||
|
||||
## Template
|
||||
|
||||
@@ -54,22 +55,23 @@ Include only the sections relevant to your change.
|
||||
|
||||
| Type | When to use | Example |
|
||||
|------|-------------|---------|
|
||||
| `patch` | Bug fixes, small improvements | Fixed crash when config missing |
|
||||
| `patch` | Release-tracked bug fixes, small improvements | Fixed crash when config missing |
|
||||
| `minor` | New features, non-breaking additions | Added `--verbose` flag |
|
||||
| `major` | Breaking changes, removed features | Renamed `init` to `setup` |
|
||||
|
||||
## When to Create a Changeset
|
||||
|
||||
**Create one for:**
|
||||
- New features or commands
|
||||
- Bug fixes that affect users
|
||||
**Use dedicated release tracking for:**
|
||||
- New features or commands selected for release
|
||||
- Notable bug fixes or hotfixes requested by a maintainer/release owner
|
||||
- Breaking changes or deprecations
|
||||
- Performance improvements users would notice
|
||||
- Performance improvements users would notice and that are planned for release
|
||||
|
||||
**Skip for:**
|
||||
**Use the normal release cadence for:**
|
||||
- Routine bug fixes that fit the normal release cadence
|
||||
- Documentation-only changes
|
||||
- Test additions/fixes
|
||||
- Internal refactoring with no user impact
|
||||
- Internal refactoring that preserves user behavior
|
||||
- CI/tooling changes
|
||||
|
||||
## Writing Good Descriptions
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
### Features
|
||||
|
||||
- **Auto-approve the OpenSpec CLI in generated skills and commands** — every generated `SKILL.md` (all tools) and every Claude Code `/opsx:*` slash command now carries `allowed-tools: Bash(openspec:*)` in its frontmatter, so agents that honor the Agent Skills standard run `openspec` commands without prompting for approval on each call; tools that don't recognize the field ignore it. Scope is limited to the `openspec` CLI; because `allowed-tools` pre-approves rather than restricts, every other tool a skill or command uses stays available under your normal permission settings.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@fission-ai/openspec": minor
|
||||
---
|
||||
|
||||
### New Features
|
||||
|
||||
- **TRAE command adapter** — Added command adapter for Trae IDE, enabling generation of `.trae/commands/opsx-<id>.md` files for custom slash commands
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **`archive` exits non-zero when blocked in human mode** — `openspec archive <change> -y` (and any non-`--json` invocation) no longer returns exit code 0 when validation fails and nothing is archived. The three blocking paths in human mode — delta-spec validation failure, spec rebuild failure, and rebuilt-spec validation failure — now set `process.exitCode = 1`, matching the existing `--json` behavior. Previously the command printed "Validation failed" (or "Aborted. No files were changed.") and exited 0, letting scripts and CI believe the archive succeeded. Aligns `archive` with the same exit-code guarantee already approved for `apply` instructions (#1250).
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **`validate` resolves changes like `status`** — `openspec validate <change>` (and `--all`/`--changes` and the interactive selector) now resolves a change by directory existence, matching `status`/`instructions`, instead of requiring `proposal.md`. A scaffolded or still-authoring change is validated rather than reported as `Unknown item`, and a resolved-but-invalid change now exits non-zero. Delta discovery also recurses the nested `specs/<area>/<capability>/spec.md` layout. (#1182)
|
||||
- **Task progress reads nested/glob `tasks.md`** — `openspec view`, `list`, and the `archive` incomplete-task gate now resolve task progress through the tracked-tasks artifact's `generates` glob (the same file-resolution `status` uses), so a change whose tasks live in nested `tasks.md` files is classified correctly and can no longer archive while unfinished. (#1202)
|
||||
- **SHALL/MUST body-keyword hint applies to main specs** — A main-spec requirement whose normative keyword sits only in the `### Requirement:` header now receives the same targeted "move it to the body line" remediation as a change delta, emitted exactly once. (#1156)
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
"@fission-ai/openspec": patch
|
||||
---
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Requirement reading fidelity** — The requirement reader used by `validate <change>`, `validate <spec>`, and `archive` is now unified into one fence-, metadata-, and multi-line-aware extraction, closing the known divergences between the change-delta path and the main-spec path (the remaining ones are documented in the change's design doc):
|
||||
- A `SHALL`/`MUST` keyword that wraps onto a later body line is detected instead of dropped (#361).
|
||||
- Metadata lines (`**ID**:`, `**Priority**:`) before the description are skipped on the spec path, matching the change path (#418). A requirement written entirely as metadata (e.g. `**Constraint**: The system MUST ...`) keeps that line as its text instead of being emptied.
|
||||
- A fenced code block before the prose line no longer becomes the requirement text (#312).
|
||||
- A `#### Scenario:` inside a fenced example no longer counts as a real scenario in `validate <change>`, matching `validate <spec>`.
|
||||
- `SHALL`/`MUST` detection uses one whole-word predicate across all readers, and a requirement with no body text falls back to its header title on both paths.
|
||||
|
||||
Displayed requirement text (e.g. in JSON output and delta descriptions) now reflects the full requirement body rather than only its first line. Archived spec content is unchanged — the archive rebuild reads raw `### Requirement:` blocks, not the parsed text.
|
||||
|
||||
- **Surface non-canonical delta headers** — `validate <change>` now emits an INFO note when an `## ADDED`/`## MODIFIED Requirements` section contains a level-3 header that is not a canonical `### Requirement:` header (one the delta reader silently skips, such as a stray `### Documentation Requirements` divider). The note never changes the `valid` result, including under `--strict` (#498).
|
||||
+81
-85
@@ -3,6 +3,8 @@ name: CI
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
merge_group:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
@@ -38,50 +40,11 @@ jobs:
|
||||
- 'scripts/update-flake.sh'
|
||||
- '.github/workflows/ci.yml'
|
||||
|
||||
test_pr:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm test
|
||||
|
||||
- name: Upload test coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-pr
|
||||
path: coverage/
|
||||
retention-days: 7
|
||||
|
||||
test_matrix:
|
||||
name: Test (${{ matrix.label }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
if: github.event_name != 'pull_request'
|
||||
if: github.event_name == 'pull_request' || github.event_name == 'merge_group' || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -89,12 +52,15 @@ jobs:
|
||||
- os: ubuntu-latest
|
||||
shell: bash
|
||||
label: linux-bash
|
||||
vitest_workers: 4
|
||||
- os: macos-latest
|
||||
shell: bash
|
||||
label: macos-bash
|
||||
vitest_workers: 4
|
||||
- os: windows-latest
|
||||
shell: pwsh
|
||||
label: windows-pwsh
|
||||
vitest_workers: 2
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -108,13 +74,11 @@ jobs:
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '20.19.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Print environment diagnostics
|
||||
@@ -128,16 +92,32 @@ jobs:
|
||||
run: pnpm run build
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
VITEST_MAX_WORKERS: ${{ matrix.vitest_workers }}
|
||||
run: pnpm test
|
||||
|
||||
- name: Upload test coverage
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-main
|
||||
name: coverage-report-${{ github.event_name }}
|
||||
path: coverage/
|
||||
retention-days: 7
|
||||
|
||||
test_pr_required:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_matrix]
|
||||
if: always() && (github.event_name == 'pull_request' || github.event_name == 'merge_group')
|
||||
steps:
|
||||
- name: Verify matrix tests passed
|
||||
run: |
|
||||
if [[ "${{ needs.test_matrix.result }}" != "success" ]]; then
|
||||
echo "Matrix test job failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "All matrix tests passed!"
|
||||
|
||||
lint:
|
||||
name: Lint & Type Check
|
||||
runs-on: ubuntu-latest
|
||||
@@ -147,13 +127,11 @@ jobs:
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '20.19.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -240,68 +218,86 @@ jobs:
|
||||
run: git checkout -- flake.nix || true
|
||||
|
||||
validate-changesets:
|
||||
name: Validate Changesets
|
||||
name: Validate Release Tracking
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request'
|
||||
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine release tracking
|
||||
id: changed-changesets
|
||||
run: |
|
||||
changed_changesets="$(git diff --name-only --diff-filter=ACMRT origin/main...HEAD -- '.changeset/*.md' ':!.changeset/README.md')"
|
||||
if [[ -n "$changed_changesets" ]]; then
|
||||
echo "has_changesets=true" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "files<<EOF"
|
||||
echo "$changed_changesets"
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_changesets=false" >> "$GITHUB_OUTPUT"
|
||||
echo "This PR follows the normal release cadence; continuing with standard validation"
|
||||
fi
|
||||
|
||||
- name: Setup pnpm
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '20.19.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Validate changesets
|
||||
- name: Validate release-tracked changesets
|
||||
if: steps.changed-changesets.outputs.has_changesets == 'true'
|
||||
env:
|
||||
CHANGESET_FILES: ${{ steps.changed-changesets.outputs.files }}
|
||||
run: |
|
||||
if command -v changeset &> /dev/null; then
|
||||
pnpm exec changeset status --since=origin/main
|
||||
else
|
||||
echo "Changesets not configured, skipping validation"
|
||||
fi
|
||||
echo "Validating changed changesets:"
|
||||
printf '%s\n' "$CHANGESET_FILES"
|
||||
pnpm exec changeset status --since=origin/main
|
||||
|
||||
required-checks-pr:
|
||||
name: All checks passed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_pr, lint, nix-flake-validate]
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: Verify all checks passed
|
||||
run: |
|
||||
if [[ "${{ needs.test_pr.result }}" != "success" ]]; then
|
||||
echo "Test job failed"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ needs.lint.result }}" != "success" ]]; then
|
||||
echo "Lint job failed"
|
||||
exit 1
|
||||
fi
|
||||
# Nix validation may be skipped if no Nix-related files changed
|
||||
if [[ "${{ needs.nix-flake-validate.result }}" != "success" && "${{ needs.nix-flake-validate.result }}" != "skipped" ]]; then
|
||||
echo "Nix flake validation job failed"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ needs.nix-flake-validate.result }}" == "skipped" ]]; then
|
||||
echo "Nix flake validation skipped (no Nix-related changes)"
|
||||
fi
|
||||
echo "All required checks passed!"
|
||||
|
||||
required-checks-main:
|
||||
name: All checks passed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_matrix, lint, nix-flake-validate]
|
||||
if: always() && github.event_name != 'pull_request'
|
||||
if: always() && (github.event_name == 'pull_request' || github.event_name == 'merge_group')
|
||||
steps:
|
||||
- name: Verify all checks passed
|
||||
run: |
|
||||
if [[ "${{ needs.test_matrix.result }}" != "success" ]]; then
|
||||
echo "Matrix test job failed"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ needs.lint.result }}" != "success" ]]; then
|
||||
echo "Lint job failed"
|
||||
exit 1
|
||||
fi
|
||||
# Nix validation may be skipped if no Nix-related files changed
|
||||
if [[ "${{ needs.nix-flake-validate.result }}" != "success" && "${{ needs.nix-flake-validate.result }}" != "skipped" ]]; then
|
||||
echo "Nix flake validation job failed"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${{ needs.nix-flake-validate.result }}" == "skipped" ]]; then
|
||||
echo "Nix flake validation skipped (no Nix-related changes)"
|
||||
fi
|
||||
echo "All required checks passed!"
|
||||
|
||||
required-checks-main:
|
||||
name: All checks passed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test_matrix, lint, nix-flake-validate]
|
||||
if: always() && github.event_name == 'push'
|
||||
steps:
|
||||
- name: Verify all checks passed
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
name: Docs site
|
||||
|
||||
# The documentation site (website/) mirrors docs/*.md via scripts/sync-docs.mjs,
|
||||
# which runs as the first step of `pnpm run build`. This workflow rebuilds that
|
||||
# mirror:
|
||||
# - on every push to main that touches docs/ or website/,
|
||||
# - manually via the Actions tab,
|
||||
# - and as a build-only check on pull requests.
|
||||
#
|
||||
# The Cloudflare Pages deploy step is temporarily disabled until setup is ready.
|
||||
# When re-enabled, deploys require two repository secrets: CLOUDFLARE_API_TOKEN
|
||||
# and CLOUDFLARE_ACCOUNT_ID. Set the site's public URL via the DOCS_SITE_URL
|
||||
# repository variable (used for OG/sitemap absolute URLs).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'website/**'
|
||||
- '.github/workflows/deploy-docs.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'website/**'
|
||||
- '.github/workflows/deploy-docs.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
# Never run two docs site jobs at once; let an in-flight job finish.
|
||||
concurrency:
|
||||
group: deploy-docs
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
# Keep enabled forks from spending CI on their own copy of this workflow.
|
||||
# PRs from forks into Fission-AI/OpenSpec still run in the base repository.
|
||||
if: ${{ github.repository == 'Fission-AI/OpenSpec' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: pnpm
|
||||
cache-dependency-path: website/pnpm-lock.yaml
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: website
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build site (mirrors docs/*.md, then next build)
|
||||
working-directory: website
|
||||
env:
|
||||
NEXT_PUBLIC_SITE_URL: ${{ vars.DOCS_SITE_URL }}
|
||||
run: pnpm run build
|
||||
|
||||
# Temporarily disabled until Cloudflare setup is ready.
|
||||
# - name: Deploy to Cloudflare Pages
|
||||
# # Only deploy from main on the canonical repo. This keeps PRs build-only,
|
||||
# # keeps forks (no secrets) build-only, and because the wrangler command
|
||||
# # below hardcodes `--branch=main` (a *production* deploy) prevents a
|
||||
# # `workflow_dispatch` on a feature branch from overwriting the live site.
|
||||
# if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && github.repository == 'Fission-AI/OpenSpec' }}
|
||||
# uses: cloudflare/wrangler-action@v3
|
||||
# with:
|
||||
# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
# workingDirectory: website
|
||||
# command: pages deploy out --project-name=openspec-docs --branch=main
|
||||
@@ -1,8 +1,9 @@
|
||||
name: Release (prepare)
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch: # manually cut a beta prerelease from main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -15,7 +16,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.repository == 'Fission-AI/OpenSpec'
|
||||
if: github.repository == 'Fission-AI/OpenSpec' && github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Generate GitHub App token first - used for checkout and changesets
|
||||
@@ -34,8 +35,6 @@ jobs:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -58,3 +57,126 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
# npm authentication handled via OIDC trusted publishing (no token needed)
|
||||
|
||||
# Manually-dispatched beta prerelease from main: version is the next stable
|
||||
# release per pending changesets with a -beta.N suffix (e.g. v1.6.0-beta.1),
|
||||
# published to npm under the `beta` dist-tag and posted as a prerelease-flagged
|
||||
# GitHub Release. Changesets are left unconsumed, so the stable flow above is
|
||||
# unaffected. This job lives in this file because npm trusted publishing
|
||||
# authorizes a single workflow file per package.
|
||||
#
|
||||
# Users opt in with: npm install -g @fission-ai/openspec@beta
|
||||
beta:
|
||||
if: github.repository == 'Fission-AI/OpenSpec' && github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24' # Node 24 includes npm 11.5.1+ required for OIDC
|
||||
cache: 'pnpm'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
# Beta version = next stable version per pending changesets, plus a
|
||||
# -beta.N suffix that increments over existing beta tags for that version.
|
||||
- name: Compute beta version
|
||||
id: version
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
git fetch --tags --force origin
|
||||
pnpm exec changeset status --output=changeset-status.json
|
||||
NEXT=$(node -p "JSON.parse(require('fs').readFileSync('changeset-status.json','utf8')).releases[0]?.newVersion ?? ''")
|
||||
rm changeset-status.json
|
||||
if [ -z "$NEXT" ]; then
|
||||
echo "No pending changesets on main - nothing to cut a beta from."
|
||||
exit 1
|
||||
fi
|
||||
N=1
|
||||
while true; do
|
||||
VERSION="${NEXT}-beta.${N}"
|
||||
TAG="v${VERSION}"
|
||||
TAG_EXISTS=false
|
||||
NPM_EXISTS=false
|
||||
RELEASE_EXISTS=false
|
||||
|
||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
||||
TAG_EXISTS=true
|
||||
fi
|
||||
if npm view "@fission-ai/openspec@${VERSION}" version >/dev/null 2>&1; then
|
||||
NPM_EXISTS=true
|
||||
fi
|
||||
if gh release view "${TAG}" >/dev/null 2>&1; then
|
||||
RELEASE_EXISTS=true
|
||||
fi
|
||||
|
||||
if [ "$TAG_EXISTS" = false ] && [ "$NPM_EXISTS" = false ] && [ "$RELEASE_EXISTS" = false ]; then
|
||||
break
|
||||
fi
|
||||
if [ "$RELEASE_EXISTS" = false ]; then
|
||||
echo "Resuming incomplete beta ${TAG}"
|
||||
break
|
||||
fi
|
||||
|
||||
N=$((N + 1))
|
||||
done
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "Cutting ${TAG}"
|
||||
|
||||
- name: Set package version
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: npm version "$VERSION" --no-git-tag-version
|
||||
|
||||
# prepublishOnly runs the build. npm authentication handled via OIDC
|
||||
# trusted publishing (no token needed).
|
||||
- name: Publish to npm under the beta dist-tag
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
if npm view "@fission-ai/openspec@${VERSION}" version >/dev/null 2>&1; then
|
||||
echo "@fission-ai/openspec@${VERSION} is already on npm; skipping publish."
|
||||
exit 0
|
||||
fi
|
||||
npm publish --tag beta
|
||||
|
||||
- name: Tag and create GitHub prerelease
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
TAG="v${VERSION}"
|
||||
HEAD_SHA=$(git rev-parse HEAD)
|
||||
|
||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
||||
TAG_SHA=$(git rev-list -n 1 "${TAG}")
|
||||
if [ "$TAG_SHA" != "$HEAD_SHA" ]; then
|
||||
echo "${TAG} already exists at ${TAG_SHA}, not current HEAD ${HEAD_SHA}."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
git tag "${TAG}"
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
|
||||
echo "${TAG} already exists on origin; skipping tag push."
|
||||
else
|
||||
git push origin "${TAG}"
|
||||
fi
|
||||
|
||||
if gh release view "${TAG}" >/dev/null 2>&1; then
|
||||
echo "GitHub Release ${TAG} already exists; skipping release creation."
|
||||
else
|
||||
gh release create "${TAG}" \
|
||||
--prerelease \
|
||||
--generate-notes \
|
||||
--title "${TAG}" \
|
||||
--notes "Beta prerelease. Install with \`npm install -g @fission-ai/openspec@beta\`."
|
||||
fi
|
||||
|
||||
+10
@@ -148,8 +148,18 @@ CLAUDE.md
|
||||
|
||||
# Pnpm
|
||||
.pnpm-store/
|
||||
/package-lock.json
|
||||
result
|
||||
|
||||
# OpenCode
|
||||
.opencode/
|
||||
opencode.json
|
||||
|
||||
# Codex
|
||||
.codex/
|
||||
|
||||
# Bob
|
||||
.bob/
|
||||
|
||||
# Trae
|
||||
.trae/
|
||||
|
||||
+114
@@ -1,5 +1,119 @@
|
||||
# @fission-ai/openspec
|
||||
|
||||
## 1.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1267](https://github.com/Fission-AI/OpenSpec/pull/1267) [`96f6cac`](https://github.com/Fission-AI/OpenSpec/commit/96f6cacb206c65bee30066f6a1f4e9b855a0d783) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- **Stores (very early beta)** — Introduces stores as a simpler way to organize specs and changes, replacing the workspace and initiative model. This feature is in very early beta — expect rough edges and breaking changes in upcoming releases.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Config parsing** — Configuration values wrapped in JSON containers are now parsed correctly.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1240](https://github.com/Fission-AI/OpenSpec/pull/1240) [`cbf386b`](https://github.com/Fission-AI/OpenSpec/commit/cbf386bd6888f103f8ff7d59b3eab98ce5b57998) Thanks [@zied-jlassi](https://github.com/zied-jlassi)! - fix(adapters): escape carriage returns in generated YAML frontmatter
|
||||
|
||||
`escapeYamlValue` flagged `\r` as a character requiring quoting but never escaped it, leaving a literal carriage return inside the double-quoted scalar where YAML line folding/normalization could silently corrupt the value (realistic with CRLF-authored command descriptions). Carriage returns are now escaped as `\r`. The helper — previously duplicated verbatim across five adapters (bob, claude, cursor, pi, windsurf) — is extracted into a shared `command-generation/yaml.ts` module so the behavior stays consistent and is fixed in one place.
|
||||
|
||||
## 1.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1165](https://github.com/Fission-AI/OpenSpec/pull/1165) [`0a01146`](https://github.com/Fission-AI/OpenSpec/commit/0a01146c181a3af8dbf645547bcbe20c0d48d615) Thanks [@TabishB](https://github.com/TabishB)! - Move beta workspace view state to `.openspec-workspace/view.yaml`, stop top-level `openspec update` from routing into workspace updates, and ignore foreign root `workspace.yaml` files so Dagster projects keep updating normally.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1003](https://github.com/Fission-AI/OpenSpec/pull/1003) [`342ed43`](https://github.com/Fission-AI/OpenSpec/commit/342ed43e694abba65a3ea275f94ba3b77df85da3) Thanks [@Miss-you](https://github.com/Miss-you)! - ### New Features
|
||||
|
||||
- **Kimi CLI support** — OpenSpec can now initialize Kimi CLI as a supported skills-only tool using `.kimi/skills/`
|
||||
|
||||
### Other
|
||||
|
||||
- Added Kimi-specific docs and init coverage aligned with skill-based `/skill:openspec-*` usage
|
||||
|
||||
- [#1154](https://github.com/Fission-AI/OpenSpec/pull/1154) [`aa16080`](https://github.com/Fission-AI/OpenSpec/commit/aa16080d16b70f7b26cebd465334b2e16c0e7a43) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- **Mistral Vibe support** — OpenSpec can now initialize Mistral Vibe as a supported skills-only tool using `.vibe/skills/`
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Case-insensitive requirement headers** — Requirement headers are now parsed regardless of capitalization, so specs no longer fail to parse over header casing
|
||||
- **Zsh completions on oh-my-zsh** — Fixed shell completion setup so tab completion installs correctly under oh-my-zsh's `compinit`
|
||||
|
||||
### Other
|
||||
|
||||
- **Clearer validation hints** — When a requirement has SHALL/MUST only in its header, `openspec validate` now points you to move the keyword onto the requirement body line instead of showing the generic error
|
||||
|
||||
- [#1030](https://github.com/Fission-AI/OpenSpec/pull/1030) [`485c97e`](https://github.com/Fission-AI/OpenSpec/commit/485c97e97d766e35dd16c02370baee2044abc4f4) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- Include the sync workflow in the default core profile so new installs generate `/opsx:sync` skills and commands by default.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1111](https://github.com/Fission-AI/OpenSpec/pull/1111) [`7fdb177`](https://github.com/Fission-AI/OpenSpec/commit/7fdb1771585b1688597d73dde5a8bc906084d0de) Thanks [@TabishB](https://github.com/TabishB)! - ### Fixed
|
||||
|
||||
- Preserve workspace planning detection when Windows short paths or symlink aliases resolve to a canonical workspace root.
|
||||
|
||||
## 1.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#995](https://github.com/Fission-AI/OpenSpec/pull/995) [`d1f3861`](https://github.com/Fission-AI/OpenSpec/commit/d1f3861d9ec694cc924b042b5da01963dcf93137) Thanks [@TabishB](https://github.com/TabishB)! - ### Bug Fixes
|
||||
|
||||
- **Canonical artifact paths** — Workflow artifact paths are now resolved via the native `realpath`, so symlinks and case-insensitive filesystems no longer cause path mismatches during apply and archive.
|
||||
- **Glob apply instructions** — Apply instructions with glob artifact outputs now resolve correctly, and literal artifact outputs are enforced to be file paths.
|
||||
- **Hidden main spec requirements** — Requirements nested inside fenced code blocks or otherwise hidden in main specs are now detected during validation.
|
||||
- **Clean `--json` output** — Spinner progress text no longer leaks into stderr when `--json` is passed, so AI agents that combine stdout and stderr can parse the JSON reliably.
|
||||
- **Silent telemetry in firewalled environments** — PostHog network errors are now swallowed with a 1s timeout and retries/remote config disabled, so OpenSpec no longer surfaces `PostHogFetchNetworkError` in locked-down networks. Telemetry opt-out is documented earlier in the README, installation guide, and CLI reference.
|
||||
|
||||
## 1.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#952](https://github.com/Fission-AI/OpenSpec/pull/952) [`cce787e`](https://github.com/Fission-AI/OpenSpec/commit/cce787ec4083da2b27781f6786f5ce0002909a7b) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- **Junie support** — Added tool and command generation for JetBrains Junie
|
||||
- **Lingma IDE support** — Added configuration support for Lingma IDE
|
||||
- **ForgeCode support** — Added tool support for ForgeCode
|
||||
- **IBM Bob support** — Added support for IBM Bob coding assistant
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Shell completions opt-in** — Completion install is now opt-in, fixing PowerShell encoding corruption
|
||||
- **Copilot auto-detection** — Prevented false GitHub Copilot detection from a bare `.github/` directory
|
||||
- **pi.dev command generation** — Fixed command reference transforms and template argument passing
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#760](https://github.com/Fission-AI/OpenSpec/pull/760) [`61eb999`](https://github.com/Fission-AI/OpenSpec/commit/61eb999f7c6c0fc98d2e7f3678756fce6a3f4378) Thanks [@fsilvaortiz](https://github.com/fsilvaortiz)! - fix: OpenCode adapter now uses `.opencode/commands/` (plural) to match OpenCode's official directory convention. Fixes #748.
|
||||
|
||||
- [#759](https://github.com/Fission-AI/OpenSpec/pull/759) [`afdca0d`](https://github.com/Fission-AI/OpenSpec/commit/afdca0d5dab1aa109cfd8848b2512333ccad60c3) Thanks [@fsilvaortiz](https://github.com/fsilvaortiz)! - fix: `openspec status` now exits gracefully when no changes exist instead of throwing a fatal error. Fixes #714.
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#747](https://github.com/Fission-AI/OpenSpec/pull/747) [`1e94443`](https://github.com/Fission-AI/OpenSpec/commit/1e94443a3551b228eecbc89e95d96d3b9600a192) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- **Profile system** — Choose between `core` (4 essential workflows) and `custom` (pick any subset) profiles to control which skills get installed. Manage profiles with the new `openspec config profile` command
|
||||
- **Propose workflow** — New one-step workflow creates a complete change proposal with design, specs, and tasks from a single request — no need to run `new` then `ff` separately
|
||||
- **AI tool auto-detection** — `openspec init` now scans your project for existing tool directories (`.claude/`, `.cursor/`, etc.) and pre-selects detected tools
|
||||
- **Pi (pi.dev) support** — Pi coding agent is now a supported tool with prompt and skill generation
|
||||
- **Kiro support** — AWS Kiro IDE is now a supported tool with prompt and skill generation
|
||||
- **Sync prunes deselected workflows** — `openspec update` now removes command files and skill directories for workflows you've deselected, keeping your project clean
|
||||
- **Config drift warning** — `openspec config list` warns when global config is out of sync with the current project
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed onboard preflight giving a false "not initialized" error on freshly initialized projects
|
||||
- Fixed archive workflow stopping mid-way when syncing — it now properly resumes after sync completes
|
||||
- Added Windows PowerShell alternatives for onboard shell commands
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -36,27 +36,28 @@ Our philosophy:
|
||||
> [!TIP]
|
||||
> **New workflow now available!** We've rebuilt OpenSpec with a new artifact-guided workflow.
|
||||
>
|
||||
> Run `/opsx:onboard` to get started. → [Learn more here](docs/opsx.md)
|
||||
> Run `/opsx:propose "your idea"` to get started. → [Learn more here](docs/opsx.md)
|
||||
|
||||
<p align="center">
|
||||
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
|
||||
</p>
|
||||
|
||||
### Teams
|
||||
|
||||
Using OpenSpec in a team? [Email here](mailto:teams@openspec.dev) for access to our Slack channel.
|
||||
|
||||
<!-- TODO: Add GIF demo of /opsx:new → /opsx:archive workflow -->
|
||||
<!-- TODO: Add GIF demo of /opsx:propose → /opsx:archive workflow -->
|
||||
|
||||
## See it in action
|
||||
|
||||
```text
|
||||
You: /opsx:new add-dark-mode
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
Ready to create: proposal
|
||||
You: /opsx:explore
|
||||
AI: What would you like to explore?
|
||||
You: I want dark mode but I'm not sure how to do it cleanly.
|
||||
AI: Let me look at your styling setup...
|
||||
Cleanest path here: CSS variables + a small theme context,
|
||||
with system-preference detection. No new dependencies. Scope it?
|
||||
You: Yes, let's do it.
|
||||
|
||||
You: /opsx:ff # "fast-forward" - generate all planning docs
|
||||
AI: ✓ proposal.md — why we're doing this, what's changing
|
||||
You: /opsx:propose add-dark-mode
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
✓ proposal.md — why we're doing this, what's changing
|
||||
✓ specs/ — requirements and scenarios
|
||||
✓ design.md — technical approach
|
||||
✓ tasks.md — implementation checklist
|
||||
@@ -84,6 +85,18 @@ AI: Archived to openspec/changes/archive/2025-01-23-add-dark-mode/
|
||||
|
||||
</details>
|
||||
|
||||
## Why teams adopt OpenSpec
|
||||
|
||||
Solo, OpenSpec keeps you and your AI honest on a single repo. On a team, the hard part moves: a feature spans the API server, the web app, and a shared library; requirements are owned by one team and consumed by others; planning starts before any code exists.
|
||||
|
||||
**[Stores](docs/stores-beta/user-guide.md)** are the answer — planning in a repo of its own. The same `openspec/` shape you already know (specs and changes), shared by `git push` like anything else. One source of truth your whole team and every coding agent can read, across every repo.
|
||||
|
||||
- **Cross-repo features** — one change, one plan, even when the code lands in three repos.
|
||||
- **Shared requirements** — a platform team owns the specs; product teams reference them read-only, right where their coding agent can read them. No drifting wiki.
|
||||
- **Plan before code** — capture the plan in the store now; the code repos catch up later.
|
||||
|
||||
> Stores are in **beta**. Start with the [Stores User Guide](docs/stores-beta/user-guide.md).
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Requires Node.js 20.19.0 or higher.**
|
||||
@@ -101,23 +114,45 @@ cd your-project
|
||||
openspec init
|
||||
```
|
||||
|
||||
Now tell your AI: `/opsx:new <what-you-want-to-build>`
|
||||
Now talk to your AI:
|
||||
|
||||
- **Not sure what to build yet?** Start with `/opsx:explore`, a no-stakes thinking partner that reads your code, weighs options, and shapes a plan before anything is written. ([Explore guide](docs/explore.md))
|
||||
- **Already know what you want?** Go straight to `/opsx:propose <what-you-want-to-build>`.
|
||||
|
||||
Both are in the default profile. If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
|
||||
|
||||
> [!NOTE]
|
||||
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 20+ tools and growing.
|
||||
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 25+ tools and growing.
|
||||
>
|
||||
> Also works with pnpm, yarn, bun, and nix. [See installation options](docs/installation.md).
|
||||
|
||||
## Docs
|
||||
|
||||
**Start here:** the **[Documentation Home](docs/README.md)** maps everything. New to OpenSpec? Read [Getting Started](docs/getting-started.md), then [How Commands Work](docs/how-commands-work.md) (where you actually type `/opsx:propose`).
|
||||
|
||||
→ **[Getting Started](docs/getting-started.md)**: first steps<br>
|
||||
→ **[Explore First](docs/explore.md)**: think it through with `/opsx:explore` before you commit<br>
|
||||
→ **[How Commands Work](docs/how-commands-work.md)**: where slash commands run vs the CLI<br>
|
||||
→ **[Core Concepts at a Glance](docs/overview.md)**: the whole mental model, one page<br>
|
||||
→ **[Examples & Recipes](docs/examples.md)**: real changes, start to finish<br>
|
||||
→ **[Workflows](docs/workflows.md)**: combos and patterns<br>
|
||||
→ **[Existing Projects](docs/existing-projects.md)**: adopt OpenSpec on a brownfield codebase<br>
|
||||
→ **[Editing a Change](docs/editing-changes.md)**: update artifacts, go back, reconcile manual edits<br>
|
||||
→ **[Commands](docs/commands.md)**: slash commands & skills<br>
|
||||
→ **[CLI](docs/cli.md)**: terminal reference<br>
|
||||
→ **[Stores](docs/stores-beta/user-guide.md)**: plan in a separate repo, shared across your team (beta)<br>
|
||||
→ **[Supported Tools](docs/supported-tools.md)**: tool integrations & install paths<br>
|
||||
→ **[Concepts](docs/concepts.md)**: how it all fits<br>
|
||||
→ **[Multi-Language](docs/multi-language.md)**: multi-language support<br>
|
||||
→ **[Customization](docs/customization.md)**: make it yours
|
||||
→ **[Customization](docs/customization.md)**: make it yours<br>
|
||||
→ **[FAQ](docs/faq.md)** · **[Troubleshooting](docs/troubleshooting.md)** · **[Glossary](docs/glossary.md)**: quick help
|
||||
|
||||
|
||||
## Community schemas
|
||||
|
||||
Third-party schema bundles distributed via standalone repositories — these provide opinionated workflows that integrate OpenSpec with other tools, similar to how [github/spec-kit's community extension catalog](https://github.com/github/spec-kit/tree/main/extensions) handles tool integrations.
|
||||
|
||||
→ **[Browse the catalog](docs/customization.md#community-schemas)** in the customization docs.
|
||||
|
||||
|
||||
## Why OpenSpec?
|
||||
@@ -127,7 +162,7 @@ AI coding assistants are powerful but unpredictable when requirements live only
|
||||
- **Agree before you build** — human and AI align on specs before code gets written
|
||||
- **Stay organized** — each change gets its own folder with proposal, specs, design, and tasks
|
||||
- **Work fluidly** — update any artifact anytime, no rigid phase gates
|
||||
- **Use your tools** — works with 20+ AI assistants via slash commands
|
||||
- **Use your tools** — works with 30+ AI assistants via slash commands
|
||||
|
||||
### How we compare
|
||||
|
||||
@@ -155,7 +190,7 @@ openspec update
|
||||
|
||||
## Usage Notes
|
||||
|
||||
**Model selection**: OpenSpec works best with high-reasoning models. We recommend Opus 4.5 and GPT 5.2 for both planning and implementation.
|
||||
**Model selection**: OpenSpec works best with high-reasoning models. We recommend Codex 5.5 and Opus 4.7 for both planning and implementation.
|
||||
|
||||
**Context hygiene**: OpenSpec benefits from a clean context window. Clear your context before starting implementation and maintain good context hygiene throughout your session.
|
||||
|
||||
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import '../dist/cli/index.js';
|
||||
import { runCli } from '../dist/cli/index.js';
|
||||
|
||||
runCli();
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
# OpenSpec Documentation
|
||||
|
||||
Welcome. This is the home for everything OpenSpec.
|
||||
|
||||
OpenSpec helps you and your AI coding assistant **agree on what to build before any code is written.** You describe the change, the AI drafts a short spec and a task list, you both look at the same plan, and then the work happens. No more discovering halfway through that the AI built the wrong thing.
|
||||
|
||||
If you read nothing else, read these two pages:
|
||||
|
||||
1. [Getting Started](getting-started.md): install, initialize, and ship your first change.
|
||||
2. [How Commands Work](how-commands-work.md): where you actually type `/opsx:propose` (hint: in your AI chat, not the terminal). This trips up almost everyone once.
|
||||
|
||||
That second one matters more than it looks. OpenSpec has two halves: a command line tool you run in your terminal, and slash commands you give to your AI assistant. Knowing which is which saves you the most common moment of confusion.
|
||||
|
||||
> **The best habit to build first: when you're not sure what to build, start with `/opsx:explore`.** It's a no-stakes thinking partner that reads your code, weighs options, and sharpens a fuzzy idea into a concrete plan before any artifact or code exists. The [Explore First](explore.md) guide makes the case.
|
||||
|
||||
## Pick your path
|
||||
|
||||
**I'm brand new.** Start with [Getting Started](getting-started.md), then skim the [Core Concepts at a Glance](overview.md). When something feels mysterious, the [FAQ](faq.md) and [Glossary](glossary.md) are nearby.
|
||||
|
||||
**I have a problem but not a plan.** This is the common case, and it has a dedicated answer: [Explore First](explore.md). Use `/opsx:explore` to think it through with the AI before committing to anything.
|
||||
|
||||
**I have a big existing codebase.** You don't document all of it. [Using OpenSpec in an Existing Project](existing-projects.md) shows how to start on real, brownfield code without boiling the ocean.
|
||||
|
||||
**I just want to get it working.** [Install](installation.md), run `openspec init`, then read [How Commands Work](how-commands-work.md) so your first slash command lands in the right place.
|
||||
|
||||
**I learn by example.** The [Examples & Recipes](examples.md) page walks through real changes start to finish: a small feature, a bug fix, a refactor, an exploration.
|
||||
|
||||
**The AI just drafted a plan — now what?** Read it. [Reviewing a Change](reviewing-changes.md) shows the two-minute pass that catches a wrong turn while it's still cheap, and [Writing Good Specs](writing-specs.md) covers what a plan worth approving is made of.
|
||||
|
||||
**I work on a team.** [OpenSpec on a Team](team-workflow.md) shows how a change maps onto a branch and a pull request, and how teammates review a plan before the code.
|
||||
|
||||
**I'm coming from the old workflow.** The [Migration Guide](migration-guide.md) explains what changed and why, and promises your existing work is safe.
|
||||
|
||||
**I want to bend it to my team's process.** [Customization](customization.md) covers project config, custom schemas, and shared context.
|
||||
|
||||
**Something's broken.** [Troubleshooting](troubleshooting.md) collects the failures people actually hit, with fixes.
|
||||
|
||||
## The whole map
|
||||
|
||||
### Start here
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [Getting Started](getting-started.md) | Install, initialize, and run your first change end to end |
|
||||
| [Explore First](explore.md) | Use `/opsx:explore` to think through an idea before you commit |
|
||||
| [How Commands Work](how-commands-work.md) | Where slash commands run, what "interactive mode" means, terminal vs chat |
|
||||
| [Core Concepts at a Glance](overview.md) | The whole mental model on one page: specs, changes, deltas, archive |
|
||||
| [Installation](installation.md) | npm, pnpm, yarn, bun, Nix, and how to verify it worked |
|
||||
|
||||
### Use it day to day
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [Workflows](workflows.md) | Common patterns and when to reach for each command |
|
||||
| [Examples & Recipes](examples.md) | Full walkthroughs of real changes, copy-pasteable |
|
||||
| [Writing Good Specs](writing-specs.md) | What a strong requirement and scenario look like, and how to right-size a change |
|
||||
| [Reviewing a Change](reviewing-changes.md) | The two-minute pass on a drafted plan before any code is written |
|
||||
| [OpenSpec on a Team](team-workflow.md) | How changes fit branches, pull requests, and review |
|
||||
| [Using OpenSpec in an Existing Project](existing-projects.md) | Adopting OpenSpec on a large brownfield codebase |
|
||||
| [Editing & Iterating on a Change](editing-changes.md) | Update artifacts, go back, reconcile manual edits |
|
||||
| [Commands](commands.md) | Reference for every `/opsx:*` slash command |
|
||||
| [CLI](cli.md) | Reference for every `openspec` terminal command |
|
||||
|
||||
### Understand it deeply
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [Concepts](concepts.md) | The long-form explanation of specs, changes, artifacts, schemas, and archive |
|
||||
| [OPSX Workflow](opsx.md) | Why the workflow is fluid instead of phase-locked, plus an architecture deep dive |
|
||||
| [Glossary](glossary.md) | Every term defined in one place |
|
||||
|
||||
### Make it yours
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [Customization](customization.md) | Project config, custom schemas, shared context |
|
||||
| [Multi-Language](multi-language.md) | Generate artifacts in languages other than English |
|
||||
| [Supported Tools](supported-tools.md) | The 25+ AI tools OpenSpec integrates with, and where files land |
|
||||
|
||||
### When you need help
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [FAQ](faq.md) | Quick answers to the questions people ask most |
|
||||
| [Troubleshooting](troubleshooting.md) | Concrete fixes for concrete failures |
|
||||
| [Migration Guide](migration-guide.md) | Moving from the legacy workflow to OPSX |
|
||||
|
||||
### Coordinate across repos (beta)
|
||||
|
||||
| Doc | What it gives you |
|
||||
|-----|-------------------|
|
||||
| [Stores: User Guide](stores-beta/user-guide.md) | Plan in its own repo when your work spans repos or teams |
|
||||
| [Agent Contract](agent-contract.md) | The machine-readable CLI surfaces agents drive |
|
||||
|
||||
## The thirty-second version
|
||||
|
||||
```text
|
||||
1. Install npm install -g @fission-ai/openspec@latest
|
||||
2. Initialize cd your-project && openspec init
|
||||
3. Explore (in your AI chat) /opsx:explore ← optional, but a great habit
|
||||
4. Propose (in your AI chat) /opsx:propose add-dark-mode
|
||||
5. Build (in your AI chat) /opsx:apply
|
||||
6. Archive (in your AI chat) /opsx:archive
|
||||
```
|
||||
|
||||
Steps 1 and 2 happen in your terminal. The rest happen in your AI assistant's chat. That split is the one thing worth memorizing, and [How Commands Work](how-commands-work.md) explains exactly why. Step 3 is optional, but starting with `/opsx:explore` when you're unsure is the habit most worth forming.
|
||||
|
||||
## Where else to get help
|
||||
|
||||
- **Discord:** [discord.gg/YctCnvvshC](https://discord.gg/YctCnvvshC) for questions, ideas, and help.
|
||||
- **GitHub Issues:** [github.com/Fission-AI/OpenSpec/issues](https://github.com/Fission-AI/OpenSpec/issues) for bugs and feature requests.
|
||||
- **`openspec feedback "your message"`** sends feedback straight from your terminal (it opens a GitHub issue).
|
||||
|
||||
Found something in these docs that's wrong, stale, or confusing? That's a bug. Open an issue or a PR. Documentation improvements are some of the most valuable contributions you can make.
|
||||
@@ -0,0 +1,137 @@
|
||||
# OpenSpec Agent Contract
|
||||
|
||||
Machine-readable surfaces of the `openspec` CLI, verified against `src/` (capstone audit, 2026-06-11). Every shape below is documented from the emitting code.
|
||||
|
||||
## 1. General conventions
|
||||
|
||||
- **One JSON document per invocation.** In `--json` mode, stdout carries exactly one JSON document (2-space pretty-printed). Human prose, spinners, and the store banner go to stderr.
|
||||
- **Store banner.** In human mode, a store-selected root prints `Using OpenSpec root: <id> (<path>)` to stderr. Never printed in JSON mode.
|
||||
- **Key casing is surface-dependent** (see Known inconsistencies): store/doctor/context payloads use `snake_case`; workflow payloads (`status`, `instructions`, `new change`, `validate`, `list`) use `camelCase`, except the embedded `root` object, which always uses `store_id`.
|
||||
- **Optional keys are omitted, not null**, in most payloads (e.g. `root.store_id`, `member.path`). Exceptions that use explicit `null` are called out per shape (store doctor `git.*`, failure payloads).
|
||||
|
||||
## 2. The diagnostic envelope
|
||||
|
||||
One envelope shape is shared by every machine-readable diagnostic (`StoreDiagnostic`):
|
||||
|
||||
```json
|
||||
{
|
||||
"severity": "error" | "warning" | "info",
|
||||
"code": "snake_case_string",
|
||||
"message": "human sentence",
|
||||
"target": "dotted.surface (optional)",
|
||||
"fix": "one actionable sentence/command (optional)"
|
||||
}
|
||||
```
|
||||
|
||||
Diagnostics appear in two positions: **status arrays** (`status: StoreDiagnostic[]` at top level or per entry) for health findings, and **thrown errors** converted to a single-element `status` array on command failure.
|
||||
|
||||
## 3. Root selection and `RootOutput`
|
||||
|
||||
All root-resolving commands (`list`, `show`, `validate`, `status`, `instructions`, `instructions apply`, `new change`, `archive`, `doctor`, `context`) resolve one OpenSpec root with one precedence:
|
||||
|
||||
1. `--store <id>` → the registered store's root (`source: "store"`).
|
||||
2. Otherwise, nearest ancestor with `openspec/`: planning shape → `source: "nearest"` (a `store:` pointer is ignored with a stderr warning); config-only dir with a valid `store:` pointer → that store, `source: "declared"`.
|
||||
3. No nearest root + registered stores exist → error `no_root_with_registered_stores`.
|
||||
4. No root, no stores: scaffolding commands treat the cwd as `source: "implicit"`; diagnostic commands (`doctor`, `context`) fail with `no_openspec_root` instead — they inspect, never scaffold.
|
||||
|
||||
Successful JSON payloads embed the root:
|
||||
|
||||
```json
|
||||
"root": { "path": "/abs/path", "source": "store" | "declared" | "nearest" | "implicit", "store_id": "id (only when store-selected)" }
|
||||
```
|
||||
|
||||
**Root-failure contract**: in JSON mode a resolution failure prints `{ ...commandNullShape, "status": [diagnostic] }` on stdout and exits 1.
|
||||
|
||||
## 4. Command JSON shapes
|
||||
|
||||
### 4.1 `list --json`
|
||||
`{ "changes": [ { "name", "completedTasks", "totalTasks", "lastModified", "status": "no-tasks"|"complete"|"in-progress" } ], "root": RootOutput }` — note the per-change `status` is a string enum here. `--specs`: `{ "specs": [ { "id", "requirementCount" } ], "root" }`.
|
||||
|
||||
### 4.2 `show <item> --json`
|
||||
Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }`.
|
||||
|
||||
### 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" }, "isComplete", "applyRequires", "artifacts": [ {id, outputPath, status: "done"|"ready"|"blocked", missingDeps?} ], "root" }`. No active changes: `{ "changes": [], "message", "root" }`, exit 0.
|
||||
|
||||
### 4.5 `instructions <artifact> --json`
|
||||
`{ "changeName", "artifactId", "schemaName", "changeDir", "planningHome"?, "outputPath", "resolvedOutputPath", "existingOutputPaths", "description", "instruction"?, "context"?, "rules"?, "references"?: ReferenceIndexEntry[], "template", "dependencies": [{id,done,path,description}], "unlocks", "root" }`.
|
||||
|
||||
`ReferenceIndexEntry`: `{ "store_id", "root"?, "specs"?: [{id,summary}], "fetch"?, "status": [] }` — resolved entries carry root/specs/fetch; unresolved carry store_id + warning status. Index capped at 50KB (`reference_index_truncated`).
|
||||
|
||||
### 4.6 `instructions apply --json`
|
||||
`{ "changeName", "changeDir", "schemaName", "contextFiles": { "<artifactId>": ["/abs", ...] }, "progress": {total,complete,remaining}, "tasks": [{id,description,done}], "state": "blocked"|"all_done"|"ready", "missingArtifacts"?, "instruction", "references"?, "root" }`.
|
||||
|
||||
### 4.7 `new change <name> --json`
|
||||
Success: `{ "change": { "id", "path", "metadataPath", "schema" }, "root" }`. Failure: `{ "change": null, "status": [d] }`, exit 1.
|
||||
|
||||
### 4.8 `archive <name> --json`
|
||||
Success: `{ "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"? }, "root" }`. Failure: `{ "archive": null, "root"?, "status": [d] }`, exit 1. JSON mode is strictly non-interactive: every prompt point becomes an `archive_*` code.
|
||||
|
||||
### 4.9 `doctor --json`
|
||||
`{ "root": { "path", "source", "store_id"?, "healthy", "status": [] }, "store": { "id", "metadata": {present,valid,remote?}, "origin_url"?, "status": [] } | null, "references": [...], "status": [] }`. Health findings of any severity exit 0. Failure payload: `{ "root": null, "store": null, "references": [], "status": [d] }`, exit 1.
|
||||
|
||||
### 4.10 `context --json`
|
||||
`{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }`. AVAILABLE = path present AND status empty. `--code-workspace <path>` writes `{folders:[{name,path}]}` (available referenced stores only, `ref:` prefixes); in JSON mode the write runs before printing so stdout holds exactly one document even on write failure. Failure: `{ "root": null, "members": [], "status": [d] }`, exit 1.
|
||||
|
||||
### 4.11 `store ... --json`
|
||||
setup/register: `{ "store": {id, root, metadata_path?}, "registry": {path, registered, already_registered}, "git": {is_repository, initialized, committed}, "created_files": [], "status": [] }`. unregister/remove: `{ "store", "registry": {path, removed}, "files": {deleted, deleted_path, left_on_disk}, "status": [] }`. list: `{ "stores": [{id, root}], "status": [] }`. doctor: `{ "stores": [ { id, root, metadata_path?, openspec_root: {...healthy, status}, metadata: {present, valid, id?, remote}, git: {is_repository, has_commits, has_uncommitted_changes, has_remote, origin_url}, status } ], "status": [] }` (`null` = unknown/not probed). Health findings exit 0; failures exit 1 with the matching null-shape. Prompt cancellation exits 130.
|
||||
|
||||
### 4.12 `schemas --json` / `templates --json`
|
||||
`schemas`: bare array `[ {name, description, artifacts, source} ]`. `templates`: keyed object `{ "<artifactId>": {path, source} }`. Both cwd-based, no root/status keys.
|
||||
|
||||
## 5. Exit-code contract
|
||||
|
||||
| Situation | Exit | Stdout |
|
||||
|---|---|---|
|
||||
| Success, incl. health findings (doctor/context/store doctor) | 0 | the payload |
|
||||
| Command failure in `--json` mode | 1 | one JSON document with `status: [d]` and the command's null-shape |
|
||||
| `validate` with failing items | 1 | full report |
|
||||
| Prompt cancellation (`store` group, human mode) | 130 | stderr only |
|
||||
|
||||
## 6. Diagnostic code catalog
|
||||
|
||||
### Resolution
|
||||
`no_openspec_root`, `no_root_with_registered_stores`, `no_registered_stores`, `unknown_store`, `store_identity_mismatch`, `unhealthy_store_root`, `store_path_not_supported`, `invalid_store_pointer`, `initiative_option_removed`, `areas_option_removed`; pass-through: `invalid_store_id`, `invalid_store_registry`, `invalid_store_metadata`.
|
||||
|
||||
### OpenSpec-root health (error, no fix)
|
||||
`openspec_store_root_missing`, `openspec_store_root_not_directory`, `openspec_root_missing`, `openspec_root_not_directory`, `openspec_config_missing`, `openspec_config_not_file`, `openspec_specs_not_directory`, `openspec_changes_not_directory`, `openspec_archive_not_directory`. During the stores beta, `openspec/specs/`, `openspec/changes/`, and `openspec/changes/archive/` may be absent in a healthy root; they are only health errors when present but not directories.
|
||||
|
||||
### Store registry/identity/state
|
||||
`invalid_store_id`, `invalid_store_registry`, `invalid_store_metadata`, `store_registry_busy`, `store_not_found`, `no_store_registry`, `store_registry_changed`, `store_metadata_missing`, `store_metadata_id_mismatch`, `store_metadata_invalid`, `store_id_conflict`, `store_path_conflict`, `store_already_registered` (info).
|
||||
|
||||
### Store setup/register/remove
|
||||
`store_setup_id_required`, `store_setup_path_required`, `store_setup_path_not_directory`, `store_setup_inside_git_repo`, `store_setup_non_empty_directory`, `store_setup_cancelled`, `store_path_required`, `store_path_missing`, `store_path_not_directory`, `store_root_pointer_declared`, `store_register_root_unhealthy`, `store_register_identity_confirmation_required`, `store_register_cancelled`, `store_remote_empty`, `store_remote_requires_hand_edit`, `store_remove_confirmation_required`, `store_remove_cancelled`, `store_remove_path_not_directory`, `store_remove_metadata_missing`, `store_root_missing` (warning in remove, error in doctor), `store_root_not_directory`.
|
||||
|
||||
### Store git
|
||||
`store_git_init_failed`, `store_git_identity_missing`, `store_git_commit_failed`, `store_git_no_commits` (warning), `store_clone_fragile_directories` (warning), `store_remote_divergence` (info, doctor).
|
||||
|
||||
### References (warning)
|
||||
`reference_invalid_id`, `reference_registry_unreadable`, `reference_unresolved`, `reference_root_unhealthy`, `reference_index_truncated`.
|
||||
|
||||
### Relationships (warning; doctor; context keeps only the registry one)
|
||||
`relationship_registry_unreadable`, `root_pointer_ignored`, `root_pointer_invalid`, `pointer_declarations_inert`.
|
||||
|
||||
### Archive (JSON mode)
|
||||
`archive_change_name_required`, `archive_change_not_found`, `archive_validation_failed`, `archive_confirmation_required`, `archive_tasks_incomplete`, `archive_spec_update_failed`, `archive_spec_validation_failed`, `archive_target_exists`, `archive_error`.
|
||||
|
||||
### Context writes
|
||||
`context_file_exists`, `context_output_dir_missing`.
|
||||
|
||||
### Fallbacks
|
||||
`doctor_failed`, `context_failed`, `store_error`, `change_error`, `archive_error`.
|
||||
|
||||
## Known inconsistencies
|
||||
|
||||
Recorded by the capstone audit; published-key renames are product decisions deferred past this release:
|
||||
|
||||
1. ~~In `--json` mode, several failure paths printed stderr only with no JSON document.~~ Fixed in the capstone gauntlet round: `show`/`validate` unknown and ambiguous items emit `{status:[{code: unknown_item | ambiguous_item, ...}]}`; thrown errors in `status`/`instructions`/`list`/`show`/`validate` route through the JSON-aware failure helper (the command's null-shape + `status`); `store <unknown subcommand> --json` emits `{status:[{code: unknown_store_subcommand}]}`; `list` carries its `{changes|specs: [], root: null}` null-shape on resolution failures.
|
||||
2. `store_root_missing` is emitted with two severities (warning in remove, error in store doctor) — context-dependent, documented above.
|
||||
3. snake_case (store family) vs camelCase (workflow family) key casing; `root.store_id` is snake_case everywhere.
|
||||
4. Four parallel envelope type declarations exist in src; archive diagnostics never carry `target`.
|
||||
5. `list --json` reuses the `status` key as a string enum per change.
|
||||
6. Only `validate` output carries a `version` field.
|
||||
7. `schemas`/`templates` ignore root selection (cwd-based, no `--store`).
|
||||
8. Deprecated noun forms (`change`/`spec` subcommands) emit unenveloped payloads without `root`/`status`.
|
||||
+282
-26
@@ -1,16 +1,20 @@
|
||||
# CLI Reference
|
||||
|
||||
The OpenSpec CLI (`openspec`) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like `/opsx:new`) documented in [Commands](commands.md).
|
||||
The OpenSpec CLI (`openspec`) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like `/opsx:propose`) documented in [Commands](commands.md).
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Commands | Purpose |
|
||||
|----------|----------|---------|
|
||||
| **Setup** | `init`, `update` | Initialize and update OpenSpec in your project |
|
||||
| **Stores (standalone OpenSpec repos)** | `store setup`, `store register`, `store unregister`, `store remove`, `store list`, `store doctor` | Manage stores — standalone OpenSpec repos you've registered |
|
||||
| **Health** | `doctor` | Report relationship health for the resolved root |
|
||||
| **Working context** | `context` | Assemble the working set (root + referenced stores) |
|
||||
| **Personal worksets** | `workset create`, `workset list`, `workset open`, `workset remove` | Keep and open personal, local working views in your tool |
|
||||
| **Browsing** | `list`, `view`, `show` | Explore changes and specs |
|
||||
| **Validation** | `validate` | Check changes and specs for issues |
|
||||
| **Lifecycle** | `archive` | Finalize completed changes |
|
||||
| **Workflow** | `status`, `instructions`, `templates`, `schemas` | Artifact-driven workflow support |
|
||||
| **Workflow** | `new change`, `status`, `instructions`, `templates`, `schemas` | Artifact-driven workflow support |
|
||||
| **Schemas** | `schema init`, `schema fork`, `schema validate`, `schema which` | Create and manage custom workflows |
|
||||
| **Config** | `config` | View and modify settings |
|
||||
| **Utility** | `feedback`, `completion` | Feedback and shell integration |
|
||||
@@ -29,6 +33,7 @@ These commands are interactive and designed for terminal use:
|
||||
|---------|---------|
|
||||
| `openspec init` | Initialize project (interactive prompts) |
|
||||
| `openspec view` | Interactive dashboard |
|
||||
| `openspec workset open <name>` | Open a saved workset (editor window or terminal agent session) |
|
||||
| `openspec config edit` | Open config in editor |
|
||||
| `openspec feedback` | Submit feedback via GitHub |
|
||||
| `openspec completion install` | Install shell completions |
|
||||
@@ -46,6 +51,16 @@ These commands support `--json` output for programmatic use by AI agents and scr
|
||||
| `openspec instructions` | Get next steps | `--json` for agent instructions |
|
||||
| `openspec templates` | Find template paths | `--json` for path resolution |
|
||||
| `openspec schemas` | List available schemas | `--json` for schema discovery |
|
||||
| `openspec store setup <id>` | Create and register a local store | `--json` with explicit inputs for structured setup output |
|
||||
| `openspec store register <path>` | Register an existing store | `--json` for structured registration output |
|
||||
| `openspec store unregister <id>` | Forget a local store registration | `--json` for structured cleanup output |
|
||||
| `openspec store remove <id>` | Delete a registered local store folder | `--yes --json` for non-interactive deletion |
|
||||
| `openspec store list` | Browse registered stores | `--json` for structured registrations |
|
||||
| `openspec store doctor` | Check local store setup | `--json` for structured diagnostics |
|
||||
| `openspec new change <id>` | Create repo-local change scaffolding | `--json`, plus `--store <id>` to use a registered store as the OpenSpec root |
|
||||
| `openspec workset create [name]` | Compose a personal working view | `--member <path> --json` for non-interactive composition |
|
||||
| `openspec workset list` | Browse saved worksets | `--json` for structured views |
|
||||
| `openspec workset remove <name>` | Delete a saved view | `--yes --json` for non-interactive removal |
|
||||
|
||||
---
|
||||
|
||||
@@ -67,6 +82,8 @@ These options work with all commands:
|
||||
|
||||
Initialize OpenSpec in your project. Creates the folder structure and configures AI tool integrations.
|
||||
|
||||
Default behavior uses global config defaults: profile `core`, delivery `both`, workflows `propose, explore, apply, sync, archive`.
|
||||
|
||||
```
|
||||
openspec init [path] [options]
|
||||
```
|
||||
@@ -83,8 +100,13 @@ openspec init [path] [options]
|
||||
|--------|-------------|
|
||||
| `--tools <list>` | Configure AI tools non-interactively. Use `all`, `none`, or comma-separated list |
|
||||
| `--force` | Auto-cleanup legacy files without prompting |
|
||||
| `--profile <profile>` | Override global profile for this init run (`core` or `custom`) |
|
||||
|
||||
**Supported tools:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codex`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `windsurf`
|
||||
`--profile custom` uses whatever workflows are currently selected in global config (`openspec config profile`).
|
||||
|
||||
**Supported tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `vibe`, `oh-my-pi`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
|
||||
|
||||
> This list mirrors `AI_TOOLS` in `src/core/config.ts`. See [Supported Tools](supported-tools.md) for each tool's skill and command paths.
|
||||
|
||||
**Examples:**
|
||||
|
||||
@@ -101,6 +123,9 @@ openspec init --tools claude,cursor
|
||||
# Configure for all supported tools
|
||||
openspec init --tools all
|
||||
|
||||
# Override profile for this run
|
||||
openspec init --profile core
|
||||
|
||||
# Skip prompts and auto-cleanup legacy files
|
||||
openspec init --force
|
||||
```
|
||||
@@ -113,8 +138,9 @@ openspec/
|
||||
├── changes/ # Proposed changes
|
||||
└── config.yaml # Project configuration
|
||||
|
||||
.claude/skills/ # Claude Code skill files (if claude selected)
|
||||
.cursor/rules/ # Cursor rules (if cursor selected)
|
||||
.claude/skills/ # Claude Code skills (if claude selected)
|
||||
.cursor/skills/ # Cursor skills (if cursor selected)
|
||||
.cursor/commands/ # Cursor OPSX commands (if delivery includes commands)
|
||||
... (other tool configs)
|
||||
```
|
||||
|
||||
@@ -122,7 +148,7 @@ openspec/
|
||||
|
||||
### `openspec update`
|
||||
|
||||
Update OpenSpec instruction files after upgrading the CLI. Re-generates AI tool configuration files.
|
||||
Update OpenSpec instruction files after upgrading the CLI. Re-generates AI tool configuration files using your current global profile, selected workflows, and delivery mode.
|
||||
|
||||
```
|
||||
openspec update [path] [options]
|
||||
@@ -150,6 +176,204 @@ openspec update
|
||||
|
||||
---
|
||||
|
||||
## Stores (standalone OpenSpec repos)
|
||||
|
||||
> **Beta.** Stores and the features built on them (references, working context, worksets) are new; command names, flags, file formats, and JSON output may change shape between releases. For the problem-first walkthrough, see the [stores guide](stores-beta/user-guide.md).
|
||||
|
||||
A store is a standalone OpenSpec repo you've registered on this machine — for example a planning repo or a contracts repo. Registering a store lets normal commands (`list`, `show`, `status`, `validate`, `new change`, `archive`, ...) act in it from anywhere by passing `--store <id>`.
|
||||
|
||||
### `openspec store setup`
|
||||
|
||||
Create and register a local store. With no arguments in a terminal,
|
||||
OpenSpec guides the user through setup. Agents and scripts should pass explicit
|
||||
inputs and use `--json`.
|
||||
|
||||
```bash
|
||||
openspec store setup [id] [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--path <path>` | Folder where the store should live (for example `~/openspec/<id>`) |
|
||||
| `--remote <url>` | Record the canonical remote in the new store's `store.yaml` |
|
||||
| `--init-git` | Initialize a Git repository with an initial commit (default) |
|
||||
| `--no-init-git` | Skip every Git action: no init, no initial commit |
|
||||
| `--json` | Output JSON |
|
||||
|
||||
Non-interactive runs (`--json`, scripts, agents) must pass both the store id and `--path`. In an interactive terminal, setup prompts for the location with an editable suggestion in a visible, user-owned place (for example `~/openspec/<id>`); it never defaults to OpenSpec's managed data directory.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
openspec store setup
|
||||
openspec store setup team-context
|
||||
openspec store setup team-context --path ~/openspec/team-context --no-init-git
|
||||
openspec store setup team-context --path ~/openspec/team-context --no-init-git --json
|
||||
```
|
||||
|
||||
### `openspec store register`
|
||||
|
||||
Register an existing local store folder. During the stores beta, a root may be
|
||||
registered before any changes exist, specs have been applied, or changes have
|
||||
been archived; in that case `openspec/changes/`, `openspec/specs/`, and
|
||||
`openspec/changes/archive/` may be absent until normal commands create them.
|
||||
A config-only repo that declares `store: <id>` remains a pointer to another
|
||||
store and is not registered as a store root unless that pointer is removed.
|
||||
|
||||
```bash
|
||||
openspec store register [path] [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--id <id>` | Store id; defaults to store metadata or folder name |
|
||||
| `--yes` | Confirm creating store identity metadata for a healthy OpenSpec root |
|
||||
| `--json` | Output JSON |
|
||||
|
||||
### `openspec store unregister`
|
||||
|
||||
Forget a local store registration without deleting files.
|
||||
|
||||
```bash
|
||||
openspec store unregister <id> [--json]
|
||||
```
|
||||
|
||||
Use this when a store was moved, cloned somewhere else, or should no longer be
|
||||
shown by OpenSpec on this machine.
|
||||
|
||||
### `openspec store remove`
|
||||
|
||||
Forget a local store registration and delete its local folder.
|
||||
|
||||
```bash
|
||||
openspec store remove <id> [--yes] [--json]
|
||||
```
|
||||
|
||||
`remove` shows the exact folder before deleting in an interactive terminal.
|
||||
Agents, scripts, and JSON callers must pass `--yes` to confirm deletion.
|
||||
OpenSpec refuses to delete a folder that does not contain matching
|
||||
store metadata.
|
||||
|
||||
### `openspec store list`
|
||||
|
||||
List locally registered stores.
|
||||
|
||||
```bash
|
||||
openspec store list [--json]
|
||||
openspec store ls [--json]
|
||||
```
|
||||
|
||||
### `openspec store doctor`
|
||||
|
||||
Check local store registration, metadata, and Git presence.
|
||||
|
||||
```bash
|
||||
openspec store doctor [id] [--json]
|
||||
```
|
||||
|
||||
Doctor is diagnostic-only; it reports missing roots, metadata mismatches, and invalid local registry state without modifying the store.
|
||||
|
||||
### Referencing stores from a project
|
||||
|
||||
A project repo can declare which stores its work draws on in `openspec/config.yaml`:
|
||||
|
||||
```yaml
|
||||
schema: spec-driven
|
||||
references:
|
||||
- team-context
|
||||
```
|
||||
|
||||
From then on, `openspec instructions` output in that repo (both the per-artifact and `apply` surfaces, JSON and human modes) carries an index of each referenced store's specs — spec ids, a one-line summary from each spec's Purpose section, and the fetch command (`openspec show <spec-id> --type spec --store <id>`). The index is built live from the registered checkout on every run; spec content is never copied into the output.
|
||||
|
||||
References are read-only context. They never change where commands act: work stays in the repo's own root, and writing to a referenced store remains an explicit `--store` action. A reference that cannot be resolved (for example, a store not registered on this machine) degrades to a warning in the index with the exact fix, and instructions still generate. `openspec doctor` reports reference health in one place.
|
||||
|
||||
### Recording where a store is cloned from
|
||||
|
||||
A store can record its canonical clone source in its committed identity file, so onboarding never dead-ends at "register the store":
|
||||
|
||||
```bash
|
||||
openspec store setup team-context --path ~/openspec/team-context \
|
||||
--remote git@github.com:acme/team-context.git
|
||||
```
|
||||
|
||||
The remote lands in `.openspec-store/store.yaml` inside the initial commit, so every clone is born knowing it. For an existing store, edit `store.yaml` by hand and commit. `store doctor` shows the recorded remote (and the checkout's observed Git origin); setup/register sharing guidance names it; and register records the checkout's origin in the machine-local registry.
|
||||
|
||||
A reference declaration can carry the clone source too, so a teammate who doesn't have the store yet gets a complete, pasteable fix (`git clone <remote> <path> && openspec store register <path> --id <id>`):
|
||||
|
||||
```yaml
|
||||
references:
|
||||
- { id: team-context, remote: "git@github.com:acme/team-context.git" }
|
||||
```
|
||||
|
||||
Recording a remote is not sync: OpenSpec never clones, pulls, or pushes on its own.
|
||||
|
||||
### Declaring a default store
|
||||
|
||||
A repo whose planning is fully externalized — no local `openspec/specs/` or `openspec/changes/` — can declare its store once instead of passing `--store` on every command:
|
||||
|
||||
```yaml
|
||||
# openspec/config.yaml (the only file under openspec/)
|
||||
store: team-context
|
||||
```
|
||||
|
||||
Normal commands then resolve to the declared store automatically; the root banner and JSON `root` block report `source: "declared"` with the store id, and printed hints still carry `--store <id>`. The declaration is a fallback, never an override: explicit `--store` always wins, and a directory with real planning folders ignores the pointer (with a warning). To convert a pointer repo into a local OpenSpec root, remove the `store:` line and run `openspec init` — init refuses to scaffold while the declaration is present.
|
||||
|
||||
## Doctor (relationship health)
|
||||
|
||||
One read-only question, one place: is the OpenSpec root healthy, and are the stores it references available on this machine?
|
||||
|
||||
```bash
|
||||
openspec doctor [--store <id>] [--json]
|
||||
```
|
||||
|
||||
The report separates root health, store metadata health (including a note when the recorded remote and the checkout's origin diverge), and reference health (the same diagnostics instructions show, with clone fixes for unresolved references). Health findings of any severity exit 0 — agents read the `status` arrays; only command failures (no root, unknown store) exit 1. Doctor never clones, syncs, or repairs. To get the assembled set itself rather than its health, use `openspec context`.
|
||||
|
||||
## Working context (the assembled set)
|
||||
|
||||
Everything this work relates to through OpenSpec declarations, in one working set: the OpenSpec root and the stores it references.
|
||||
|
||||
```bash
|
||||
openspec context [--store <id>] [--json] [--code-workspace <path> [--force]]
|
||||
```
|
||||
|
||||
The JSON brief is agent-consumable (each available referenced store carries its fetch recipe; unresolved members carry the same fixes instructions and doctor show). `--code-workspace` additionally writes a VS Code workspace file containing the root plus the available referenced stores (`ref:<id>` folders) — the one write this command performs, refused without `--force` if the file exists. Unavailable members are reported, never guessed at.
|
||||
|
||||
"Working context" is the assembled set; the `context:` field in `openspec/config.yaml` is project background injected into instructions — two different things. `openspec doctor` answers whether the set is healthy; `openspec context` answers what the set is.
|
||||
|
||||
## Personal worksets
|
||||
|
||||
> **Beta.** Worksets are part of the new beta surface; commands, flags, and file formats may change shape between releases. For the walkthrough, see the [stores guide](stores-beta/user-guide.md#worksets-reopen-the-folders-you-work-on-together).
|
||||
|
||||
A workset is a personal, named view of the folders you work on together — a planning root plus whatever else you choose — kept on your machine and reopened by name in your tool. It is purely local: never committed, never shared, never derived from declarations, and removing one never touches a member folder.
|
||||
|
||||
```bash
|
||||
openspec workset create [name] [--member <path> | --member <name>=<path>]... [--tool <id>] [--json]
|
||||
openspec workset list [--json]
|
||||
openspec workset open <name> [--tool <id>]
|
||||
openspec workset remove <name> [--yes] [--json]
|
||||
```
|
||||
|
||||
`create` runs a short guided flow (or takes `--member` flags non-interactively; the first member is the primary — sessions start there). `open` launches the chosen tool: editors (VS Code, Cursor) open a window with every member and return; CLI agents (Claude Code, codex) take over this terminal as a session with every member attached and no prompt pre-filled, ending when you exit. A member folder missing at open time is skipped with a note; the rest opens. The saved tool preference is overridable per open with `--tool`.
|
||||
|
||||
Supporting a new tool is configuration, not code. Every tool is one of two launch styles — `workspace-file` (launched with the generated `.code-workspace`) or `attach-dirs` (one attach flag per member) — and the `openers` key in the global `config.json` (open it with `openspec config edit`) adds tools or adjusts built-ins per field:
|
||||
|
||||
```json
|
||||
{
|
||||
"openers": {
|
||||
"zed": { "style": "workspace-file" },
|
||||
"claude": { "attach_flag": "--dir" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All workset state lives under the global data dir's `worksets/` folder (the saved views plus the generated `<name>.code-workspace` files, regenerated on every open); deleting that folder removes every trace.
|
||||
|
||||
---
|
||||
|
||||
## Browsing Commands
|
||||
|
||||
### `openspec list`
|
||||
@@ -185,9 +409,8 @@ openspec list --json
|
||||
**Output (text):**
|
||||
|
||||
```
|
||||
Active changes:
|
||||
add-dark-mode UI theme switching support
|
||||
fix-login-bug Session timeout handling
|
||||
Changes:
|
||||
add-dark-mode No tasks just now
|
||||
```
|
||||
|
||||
---
|
||||
@@ -394,6 +617,38 @@ openspec archive update-ci-config --skip-specs
|
||||
|
||||
These commands support the artifact-driven OPSX workflow. They're useful for both humans checking progress and agents determining next steps.
|
||||
|
||||
### `openspec new change`
|
||||
|
||||
Create a change directory and optional checked-in metadata in the resolved OpenSpec root.
|
||||
|
||||
```bash
|
||||
openspec new change <name> [options]
|
||||
```
|
||||
|
||||
Change names must use lowercase kebab-case. They start with a lowercase letter,
|
||||
then contain lowercase letters, numbers, and single hyphens. They cannot start
|
||||
with a number, contain spaces, underscores, uppercase letters, consecutive
|
||||
hyphens, or leading/trailing hyphens. When including an external ticket ID,
|
||||
prefix it with a word, for example `ticket-123-add-notifications` instead of
|
||||
`123-add-notifications`.
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--description <text>` | Description to add to `README.md` |
|
||||
| `--goal <text>` | Optional goal metadata to store with the change |
|
||||
| `--schema <name>` | Workflow schema to use |
|
||||
| `--store <id>` | Store id to use as the OpenSpec root (a store is a standalone OpenSpec repo you've registered) |
|
||||
| `--json` | Output JSON |
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
openspec new change add-billing-api
|
||||
openspec new change add-billing-api --store team-context --json
|
||||
```
|
||||
|
||||
### `openspec status`
|
||||
|
||||
Display artifact completion status for a change.
|
||||
@@ -428,29 +683,28 @@ openspec status --change add-dark-mode --json
|
||||
```
|
||||
Change: add-dark-mode
|
||||
Schema: spec-driven
|
||||
Progress: 2/4 artifacts complete
|
||||
|
||||
Artifacts:
|
||||
✓ proposal proposal.md exists
|
||||
✓ specs specs/ exists
|
||||
◆ design ready (requires: specs)
|
||||
○ tasks blocked (requires: design)
|
||||
|
||||
Next: Create design using /opsx:continue
|
||||
[x] proposal
|
||||
[ ] design
|
||||
[x] specs
|
||||
[-] tasks (blocked by: design)
|
||||
```
|
||||
|
||||
**Output (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"change": "add-dark-mode",
|
||||
"schema": "spec-driven",
|
||||
"changeName": "add-dark-mode",
|
||||
"schemaName": "spec-driven",
|
||||
"isComplete": false,
|
||||
"applyRequires": ["tasks"],
|
||||
"artifacts": [
|
||||
{"id": "proposal", "status": "complete", "path": "proposal.md"},
|
||||
{"id": "specs", "status": "complete", "path": "specs/"},
|
||||
{"id": "design", "status": "ready", "requires": ["specs"]},
|
||||
{"id": "tasks", "status": "blocked", "requires": ["design"]}
|
||||
],
|
||||
"next": "design"
|
||||
{"id": "proposal", "outputPath": "proposal.md", "status": "done"},
|
||||
{"id": "design", "outputPath": "design.md", "status": "ready"},
|
||||
{"id": "specs", "outputPath": "specs/**/*.md", "status": "done"},
|
||||
{"id": "tasks", "outputPath": "tasks.md", "status": "blocked", "missingDeps": ["design"]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -810,7 +1064,7 @@ openspec config profile core
|
||||
- Keep current settings (exit)
|
||||
|
||||
If you keep current settings, no changes are written and no update prompt is shown.
|
||||
If there are no config changes but the current project files are out of sync with your global profile/delivery, OpenSpec will show a warning and suggest running `openspec update`.
|
||||
If there are no config changes but the current project files are out of sync with your global profile/delivery, OpenSpec will show a warning and suggest `openspec update`.
|
||||
Pressing `Ctrl+C` also cancels the flow cleanly (no stack trace) and exits with code `130`.
|
||||
In the workflow checklist, `[x]` means the workflow is selected in global config. To apply those selections to project files, run `openspec update` (or choose `Apply changes to this project now?` when prompted inside a project).
|
||||
|
||||
@@ -912,6 +1166,8 @@ openspec completion uninstall
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OPENSPEC_TELEMETRY` | Set to `0` to disable telemetry |
|
||||
| `DO_NOT_TRACK` | Set to `1` to disable telemetry (standard DNT signal) |
|
||||
| `OPENSPEC_CONCURRENCY` | Default concurrency for bulk validation (default: 6) |
|
||||
| `EDITOR` or `VISUAL` | Editor for `openspec config edit` |
|
||||
| `NO_COLOR` | Disable color output when set |
|
||||
@@ -920,7 +1176,7 @@ openspec completion uninstall
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Commands](commands.md) - AI slash commands (`/opsx:new`, `/opsx:apply`, etc.)
|
||||
- [Commands](commands.md) - AI slash commands (`/opsx:propose`, `/opsx:apply`, etc.)
|
||||
- [Workflows](workflows.md) - Common patterns and when to use each command
|
||||
- [Customization](customization.md) - Create custom schemas and templates
|
||||
- [Getting Started](getting-started.md) - First-time setup guide
|
||||
|
||||
+116
-13
@@ -6,25 +6,75 @@ For workflow patterns and when to use each command, see [Workflows](workflows.md
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Default Quick Path (`core` profile)
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/opsx:propose` | Create a change and generate planning artifacts in one step |
|
||||
| `/opsx:explore` | Think through ideas before committing to a change |
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create the next artifact based on dependencies |
|
||||
| `/opsx:ff` | Fast-forward: create all planning artifacts at once |
|
||||
| `/opsx:apply` | Implement tasks from the change |
|
||||
| `/opsx:verify` | Validate implementation matches artifacts |
|
||||
| `/opsx:update` | Revise a change's planning artifacts and keep them coherent |
|
||||
| `/opsx:sync` | Merge delta specs into main specs |
|
||||
| `/opsx:archive` | Archive a completed change |
|
||||
|
||||
### Expanded Workflow Commands (custom workflow selection)
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/opsx:new` | Start a new change scaffold |
|
||||
| `/opsx:continue` | Create the next artifact based on dependencies |
|
||||
| `/opsx:ff` | Fast-forward: create all planning artifacts at once |
|
||||
| `/opsx:verify` | Validate implementation matches artifacts |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes at once |
|
||||
| `/opsx:onboard` | Guided tutorial through the complete workflow |
|
||||
|
||||
The default global profile is `core`. To enable expanded workflow commands, run `openspec config profile`, select workflows, then run `openspec update` in your project.
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
### `/opsx:propose`
|
||||
|
||||
Create a new change and generate planning artifacts in one step. This is the default start command in the `core` profile.
|
||||
|
||||
**Syntax:**
|
||||
```text
|
||||
/opsx:propose [change-name-or-description]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name-or-description` | No | Kebab-case name or plain-language change description |
|
||||
|
||||
**What it does:**
|
||||
- Creates `openspec/changes/<change-name>/`
|
||||
- Generates artifacts needed before implementation (for `spec-driven`: proposal, specs, design, tasks)
|
||||
- Stops when the change is ready for `/opsx:apply`
|
||||
|
||||
**Example:**
|
||||
```text
|
||||
You: /opsx:propose add-dark-mode
|
||||
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
✓ proposal.md
|
||||
✓ specs/ui/spec.md
|
||||
✓ design.md
|
||||
✓ tasks.md
|
||||
Ready for implementation. Run /opsx:apply.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Use this for the fastest end-to-end path
|
||||
- If you want step-by-step artifact control, enable expanded workflows and use `/opsx:new` + `/opsx:continue`
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:explore`
|
||||
|
||||
> **Start here when you're unsure.** Explore is a no-stakes thinking partner: it reads your codebase, compares options, and sharpens a fuzzy idea into a concrete plan before any change exists. It ships in the default profile. For the full case and more examples, see the [Explore First](explore.md) guide.
|
||||
|
||||
Think through ideas, investigate problems, and clarify requirements before committing to a change.
|
||||
|
||||
**Syntax:**
|
||||
@@ -42,7 +92,7 @@ Think through ideas, investigate problems, and clarify requirements before commi
|
||||
- Investigates the codebase to answer questions
|
||||
- Compares options and approaches
|
||||
- Creates visual diagrams to clarify thinking
|
||||
- Can transition to `/opsx:new` when insights crystallize
|
||||
- Can transition to `/opsx:propose` (default) or `/opsx:new` (expanded workflow) when insights crystallize
|
||||
|
||||
**Example:**
|
||||
```text
|
||||
@@ -66,7 +116,7 @@ AI: Let me investigate your current auth setup...
|
||||
|
||||
You: Let's go with JWT. Can we start a change for that?
|
||||
|
||||
AI: Ready when you are. Run /opsx:new add-jwt-auth to begin.
|
||||
AI: Ready when you are. Run /opsx:propose add-jwt-auth to begin.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
@@ -79,7 +129,9 @@ AI: Ready when you are. Run /opsx:new add-jwt-auth to begin.
|
||||
|
||||
### `/opsx:new`
|
||||
|
||||
Start a new change. Creates the change folder structure and scaffolds it with the selected schema.
|
||||
Start a new change scaffold. Creates the change folder and waits for you to generate artifacts with `/opsx:continue` or `/opsx:ff`.
|
||||
|
||||
This command is part of the expanded workflow set (not included in the default `core` profile).
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
@@ -266,6 +318,55 @@ AI: Implementing add-dark-mode...
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:update`
|
||||
|
||||
Revise a change's existing planning artifacts and keep them coherent with one another. Planning artifacts only - it never edits code.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
```text
|
||||
/opsx:update [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to update (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
|
||||
- Reads the change's artifacts via `openspec status --change <name> --json`
|
||||
- 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)
|
||||
|
||||
**Example:**
|
||||
|
||||
```text
|
||||
You: /opsx:update add-dark-mode - we're storing the theme in a cookie now, not localStorage
|
||||
|
||||
AI: Reading add-dark-mode artifacts...
|
||||
|
||||
The design references localStorage in two places; tasks 1.3 covers
|
||||
localStorage persistence; the proposal doesn't mention storage.
|
||||
|
||||
Proposed revisions:
|
||||
1. design.md - swap localStorage decision for cookie storage
|
||||
2. tasks.md - reword task 1.3 to cookie persistence
|
||||
|
||||
Apply revision 1? (design.md)
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
|
||||
- It won't create missing artifacts - that's `/opsx:continue`
|
||||
- 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))
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:verify`
|
||||
|
||||
Validate that implementation matches your change artifacts. Checks completeness, correctness, and coherence.
|
||||
@@ -565,13 +666,15 @@ Different AI tools use slightly different command syntax. Use the format that ma
|
||||
|
||||
| Tool | Syntax Example |
|
||||
|------|----------------|
|
||||
| Claude Code | `/opsx:new`, `/opsx:apply` |
|
||||
| Cursor | `/opsx-new`, `/opsx-apply` |
|
||||
| Windsurf | `/opsx-new`, `/opsx-apply` |
|
||||
| Copilot (IDE) | `/opsx-new`, `/opsx-apply` |
|
||||
| Trae | `/openspec-new-change`, `/openspec-apply-change` |
|
||||
| Claude Code | `/opsx:propose`, `/opsx:apply` |
|
||||
| Cursor | `/opsx-propose`, `/opsx-apply` |
|
||||
| Windsurf | `/opsx-propose`, `/opsx-apply` |
|
||||
| Copilot (IDE) | `/opsx-propose`, `/opsx-apply` |
|
||||
| Oh My Pi | `/opsx-propose`, `/opsx-apply` |
|
||||
| Kimi CLI | Skill-based invocations such as `/skill:openspec-propose`, `/skill:openspec-apply-change` (no generated `opsx-*` command files) |
|
||||
| Trae | `/opsx-propose`, `/opsx-apply` |
|
||||
|
||||
The functionality is identical regardless of syntax.
|
||||
The intent is the same across tools, but how commands are surfaced can differ by integration.
|
||||
|
||||
> **Note:** GitHub Copilot commands (`.github/prompts/*.prompt.md`) are only available in IDE extensions (VS Code, JetBrains, Visual Studio). GitHub Copilot CLI does not currently support custom prompt files — see [Supported Tools](supported-tools.md) for details and workarounds.
|
||||
|
||||
|
||||
+27
-27
@@ -7,10 +7,10 @@ This guide explains the core ideas behind OpenSpec and how they fit together. Fo
|
||||
OpenSpec is built around four principles:
|
||||
|
||||
```
|
||||
fluid not rigid — no phase gates, work on what makes sense
|
||||
fluid not rigid — no phase gates, work on what makes sense
|
||||
iterative not waterfall — learn as you build, refine as you go
|
||||
easy not complex — lightweight setup, minimal ceremony
|
||||
brownfield-first — works with existing codebases, not just greenfield
|
||||
easy not complex — lightweight setup, minimal ceremony
|
||||
brownfield-first — works with existing codebases, not just greenfield
|
||||
```
|
||||
|
||||
### Why These Principles Matter
|
||||
@@ -28,19 +28,19 @@ brownfield-first — works with existing codebases, not just greenfield
|
||||
OpenSpec organizes your work into two main areas:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ openspec/ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌──────────────────────────────┐ │
|
||||
│ │ specs/ │ │ changes/ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Source of truth │◄─────│ Proposed modifications │ │
|
||||
│ │ How your system │ merge│ Each change = one folder │ │
|
||||
│ │ currently works │ │ Contains artifacts + deltas │ │
|
||||
│ │ │ │ │ │
|
||||
│ └─────────────────────┘ └──────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ openspec/ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ ┌───────────────────────────────┐ │
|
||||
│ │ specs/ │ │ changes/ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Source of truth │◄─────│ Proposed modifications │ │
|
||||
│ │ How your system │ merge│ Each change = one folder │ │
|
||||
│ │ currently works │ │ Contains artifacts + deltas │ │
|
||||
│ │ │ │ │ │
|
||||
│ └─────────────────────┘ └───────────────────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Specs** are the source of truth — they describe how your system currently behaves.
|
||||
@@ -270,7 +270,7 @@ Delta specs describe **what's changing** relative to the current specs. See [Del
|
||||
|
||||
The design captures **technical approach** and **architecture decisions**.
|
||||
|
||||
```markdown
|
||||
````markdown
|
||||
# Design: Add Dark Mode
|
||||
|
||||
## Technical Approach
|
||||
@@ -306,7 +306,7 @@ CSS Variables (applied to :root)
|
||||
- `src/contexts/ThemeContext.tsx` (new)
|
||||
- `src/components/ThemeToggle.tsx` (new)
|
||||
- `src/styles/globals.css` (modified)
|
||||
```
|
||||
````
|
||||
|
||||
**When to update the design:**
|
||||
- Implementation reveals the approach won't work
|
||||
@@ -558,17 +558,17 @@ openspec/
|
||||
## How It All Fits Together
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ OPENSPEC FLOW │
|
||||
│ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 1. START │ /opsx:new creates a change folder │
|
||||
│ │ 1. START │ /opsx:propose (core) or /opsx:new (expanded) │
|
||||
│ │ CHANGE │ │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 2. CREATE │ /opsx:ff or /opsx:continue │
|
||||
│ │ 2. CREATE │ /opsx:ff or /opsx:continue (expanded workflow) │
|
||||
│ │ ARTIFACTS │ Creates proposal → specs → design → tasks │
|
||||
│ │ │ (based on schema dependencies) │
|
||||
│ └───────┬────────┘ │
|
||||
@@ -587,13 +587,13 @@ openspec/
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ 5. ARCHIVE │────►│ Delta specs merge into main specs │ │
|
||||
│ │ CHANGE │ │ Change folder moves to archive/ │ │
|
||||
│ └────────────────┘ │ Specs are now the updated source of truth │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ ┌────────────────┐ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ 5. ARCHIVE │────►│ Delta specs merge into main specs │ │
|
||||
│ │ CHANGE │ │ Change folder moves to archive/ │ │
|
||||
│ └────────────────┘ │ Specs are now the updated source of truth │ │
|
||||
│ └──────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**The virtuous cycle:**
|
||||
|
||||
@@ -337,6 +337,20 @@ Then edit `schema.yaml` to add:
|
||||
|
||||
---
|
||||
|
||||
## Community Schemas
|
||||
|
||||
OpenSpec also supports community-maintained schemas distributed via standalone repositories. These provide opinionated workflows that integrate OpenSpec with other tools or systems, similar to how [github/spec-kit's community extension catalog](https://github.com/github/spec-kit/tree/main/extensions) works for spec-kit.
|
||||
|
||||
Community schemas are not vendored into OpenSpec core — they live in their own repositories with their own release cadence. To use one, copy the schema bundle into your project's `openspec/schemas/<schema-name>/` directory (each repo's README has install instructions).
|
||||
|
||||
| Schema | Maintainer | Repository | Description |
|
||||
|--------|-----------|-----------|-------------|
|
||||
| `superpowers-bridge` | @JiangWay | [JiangWay/openspec-schemas](https://github.com/JiangWay/openspec-schemas/tree/main/superpowers-bridge) | Integrates OpenSpec's artifact governance with [obra/superpowers](https://github.com/obra/superpowers) execution skills (brainstorming, writing-plans, TDD via subagents, code review, finishing). Adds an evidence-first `retrospective` artifact filling a gap Superpowers does not natively cover. |
|
||||
|
||||
> Want to contribute a community schema? Open an issue with a link to your repository, or submit a PR adding a row to this table.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [CLI Reference: Schema Commands](cli.md#schema-commands) - Full command documentation
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Editing & Iterating on a Change
|
||||
|
||||
**Every artifact in a change is just a Markdown file you can edit at any time.** There is no locked "planning phase," no approval gate, no special edit mode to enter. Want to change the proposal after you've started building? Open `proposal.md` and change it. Realized the design is wrong mid-implementation? Fix `design.md` and keep going. That's the whole answer, and it's by design.
|
||||
|
||||
This page is for the moment you think "wait, can I go back and change that?" Yes. Here's how, for each common case.
|
||||
|
||||
## Two ways to edit anything
|
||||
|
||||
You always have both:
|
||||
|
||||
1. **Edit the file directly.** Artifacts are plain Markdown in `openspec/changes/<name>/`. Open `proposal.md`, `design.md`, `tasks.md`, or a delta spec under `specs/` in your editor and change it. Nothing else is required.
|
||||
|
||||
2. **Ask your AI to revise it.** In chat, just say what you want: "Update the proposal to drop the caching idea and add a rate-limit section," or "the design should use a queue, not polling." The AI edits the artifact for you, using the rest of the change as context.
|
||||
|
||||
Use whichever fits the moment. Small wording tweak? Edit the file. Substantive rethink? Let the AI revise with full context.
|
||||
|
||||
## "How do I update the proposal (or specs) after I've started?"
|
||||
|
||||
Just update it. Same change, refined.
|
||||
|
||||
If you're using the expanded commands, the natural flow is: edit the artifact, then run `/opsx:continue` to pick up from the new state, or `/opsx:apply` to keep implementing against the updated plan. If you're on the default `core` commands, edit the artifact and run `/opsx:apply`; it reads the current files, so it builds against whatever the artifacts now say.
|
||||
|
||||
The mental model: artifacts are the live plan, not a signed contract. The AI always works from their current contents, so editing them steers the work.
|
||||
|
||||
```text
|
||||
You: I want to change the approach in this change.
|
||||
|
||||
You: [edit design.md, or tell the AI:]
|
||||
Update design.md to use a background job instead of a synchronous call.
|
||||
|
||||
AI: Updated design.md. The task list still fits; want me to continue applying?
|
||||
|
||||
You: /opsx:apply
|
||||
```
|
||||
|
||||
This answers a very common question: there's no separate "update proposal" command because you don't need one. The file is the source of truth, and editing it (by hand or via the AI) is the update.
|
||||
|
||||
## "How do I go back to review after implementing?"
|
||||
|
||||
You don't have to "go back," because you never left. The workflow is fluid: review, edit, and implementation aren't sequential phases you're trapped in.
|
||||
|
||||
Concretely, after some `/opsx:apply` work:
|
||||
|
||||
- Want to re-examine the plan? Open the artifacts and read them, or run `openspec show <change>` in your terminal for a consolidated view.
|
||||
- Found something to change? Edit the artifact (or ask the AI to), then continue.
|
||||
- Want a structured check that the code matches the plan? Run `/opsx:verify` (expanded command). It reports completeness, correctness, and coherence without blocking anything. See [Workflows: Verify](workflows.md#verify-check-your-work).
|
||||
|
||||
There's no "review phase" to return to, because review is something you can do at any point, including after implementation.
|
||||
|
||||
## "I edited the code by hand. How do I reconcile that with OpenSpec?"
|
||||
|
||||
This happens constantly and it's fine. You tweaked something in your editor, and now the code and the artifacts disagree. Bring them back in sync in whichever direction is true:
|
||||
|
||||
- **The code is now correct, the spec is stale.** Update the delta spec (and tasks, if relevant) to describe the behavior you actually shipped. The spec should match reality before you archive, because archiving merges the spec into your source of truth.
|
||||
- **The spec is correct, the code drifted.** Keep building or fixing until the code matches the spec.
|
||||
|
||||
A fast way to surface mismatches is `/opsx:verify`: it reads your artifacts and your code and tells you where they diverge. Treat its output as a to-do list for reconciliation, then archive once they agree.
|
||||
|
||||
The principle: at archive time, your specs become the truth of record. So before you archive, make the specs honest about what the code does. Manual edits are welcome; just don't let them quietly desync the spec.
|
||||
|
||||
## Refining a proposal you're not happy with
|
||||
|
||||
If a generated proposal misses the mark, you have three good moves:
|
||||
|
||||
- **Iterate in place.** Tell the AI what's off ("the scope is too broad, drop the admin features") and let it revise. Cheapest and usually right.
|
||||
- **Explore first, then re-propose.** If the problem is that the idea itself is unclear, step back to `/opsx:explore`, think it through, and let a sharper proposal come out of that. See [Explore First](explore.md).
|
||||
- **Start fresh.** If the intent has fundamentally changed, a new change can be clearer than patching the old one.
|
||||
|
||||
That last move has its own decision guide, next.
|
||||
|
||||
## When to update vs. start a new change
|
||||
|
||||
Short version: **update when it's the same work refined; start new when the intent fundamentally changed or the scope exploded into different work.**
|
||||
|
||||
- Same goal, better approach? Update.
|
||||
- Scope narrowing (ship the MVP now, more later)? Update, then archive, then a new change for phase two.
|
||||
- The problem itself changed ("add dark mode" became "build a full theming system")? New change.
|
||||
|
||||
There's a full flowchart and worked examples in [Workflows: When to Update vs Start Fresh](workflows.md#when-to-update-vs-start-fresh) and a deeper treatment in [OPSX: When to Update vs. Start Fresh](opsx.md#when-to-update-vs-start-fresh).
|
||||
|
||||
## A note on tasks
|
||||
|
||||
`tasks.md` is a living checklist, not a frozen plan. As you implement, you can add tasks you discover, remove ones that turned out unnecessary, or reorder them. The AI checks items off as it completes them during `/opsx:apply`, and it resumes from the first unchecked task if you come back later. Editing the list mid-flight is expected.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Workflows](workflows.md) - patterns, plus the update-vs-new decision guide
|
||||
- [Reviewing a Change](reviewing-changes.md) - the two-minute pass on a plan before you build it
|
||||
- [Explore First](explore.md) - the place to step back to when an idea needs rethinking
|
||||
- [Commands](commands.md) - `/opsx:continue`, `/opsx:apply`, and `/opsx:verify` in detail
|
||||
- [Concepts: Artifacts](concepts.md#artifacts) - what each artifact is for
|
||||
@@ -0,0 +1,215 @@
|
||||
# Examples & Recipes
|
||||
|
||||
Real changes, start to finish. Each recipe shows the commands you'd type and what you'd see back, so you can match your situation to a pattern and copy it. These use the default **core** commands (`propose`, `explore`, `apply`, `sync`, `archive`); where the expanded set helps, it's noted.
|
||||
|
||||
A reminder before you start: slash commands like `/opsx:propose` go in your **AI assistant's chat**, and `openspec` commands go in your **terminal**. If that's new, read [How Commands Work](how-commands-work.md) first. In the transcripts below, `You:` and `AI:` are the chat, and lines starting with `$` are the terminal.
|
||||
|
||||
> **Not sure what you're building yet?** Most of these recipes are sharper if you start with `/opsx:explore` to think it through first. [Recipe 3](#recipe-3-exploring-before-you-commit) shows it in action, and the [Explore First](explore.md) guide makes the full case.
|
||||
|
||||
## Recipe 1: A small feature, the fast path
|
||||
|
||||
**When to use it:** you know what you want, and it's a contained piece of work. This is the most common recipe.
|
||||
|
||||
The whole thing is three commands. Propose, build, archive.
|
||||
|
||||
```text
|
||||
You: /opsx:propose add-logout-button
|
||||
|
||||
AI: Created openspec/changes/add-logout-button/
|
||||
✓ proposal.md — why and what
|
||||
✓ specs/ — the new requirement and its scenarios
|
||||
✓ design.md — technical approach
|
||||
✓ tasks.md — implementation checklist
|
||||
Ready for implementation. Run /opsx:apply.
|
||||
```
|
||||
|
||||
Now read the plan. Open the proposal and the delta spec. This is the moment OpenSpec is built for: catching a wrong assumption while it's still one paragraph, not 400 lines of code. Edit any artifact directly if something's off, then continue.
|
||||
|
||||
```text
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Working through tasks...
|
||||
✓ 1.1 Add logout button to header
|
||||
✓ 1.2 Clear session on click
|
||||
✓ 1.3 Add confirmation dialog
|
||||
All tasks complete!
|
||||
|
||||
You: /opsx:archive
|
||||
|
||||
AI: Archiving add-logout-button...
|
||||
✓ Merged specs into openspec/specs/auth/spec.md
|
||||
✓ Moved to openspec/changes/archive/2026-06-22-add-logout-button/
|
||||
Done. Ready for the next change.
|
||||
```
|
||||
|
||||
That's it. The logout behavior is now part of your specs, and the change is filed away with its full context.
|
||||
|
||||
## Recipe 2: A bug fix
|
||||
|
||||
**When to use it:** something is broken and you want the fix recorded as a deliberate change to behavior, not a mystery commit.
|
||||
|
||||
Bug fixes work exactly like features. The difference is in how you frame the proposal: describe the *correct* behavior, not just "fix the bug."
|
||||
|
||||
```text
|
||||
You: /opsx:propose fix-login-redirect-loop
|
||||
|
||||
AI: Created openspec/changes/fix-login-redirect-loop/
|
||||
The proposal captures the current broken behavior and the
|
||||
intended behavior. The delta spec MODIFIES the redirect
|
||||
requirement so it's now precise and testable.
|
||||
Ready for implementation.
|
||||
```
|
||||
|
||||
Because the fix lands as a `MODIFIED` requirement with a fresh scenario, the next person (or the next AI session) sees not just that you fixed it, but what "correct" means. Then `/opsx:apply` and `/opsx:archive` as usual.
|
||||
|
||||
Tip: for a fix, a good scenario is the regression test in prose. "GIVEN a logged-out user, WHEN they submit valid credentials, THEN they land on the dashboard and are not redirected again." Write that, and the implementation has a clear target.
|
||||
|
||||
## Recipe 3: Exploring before you commit
|
||||
|
||||
**When to use it:** you have a problem but not yet a plan. You're not sure what to build, or which approach is right.
|
||||
|
||||
Start with `/opsx:explore`. It's a thinking partner with no structure and no artifacts created. It reads your codebase and helps you decide.
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: Page loads feel slow but I don't know where the time goes.
|
||||
|
||||
AI: Let me look... I found three likely culprits:
|
||||
1. Large unoptimized images on the product list
|
||||
2. Synchronous data fetching that blocks render
|
||||
3. Re-renders triggered by context changes
|
||||
Which should we tackle first?
|
||||
|
||||
You: The data fetching one.
|
||||
|
||||
You: /opsx:propose optimize-product-list-fetching
|
||||
|
||||
AI: Created the change, with a proposal informed by what we just
|
||||
found. Ready for implementation.
|
||||
```
|
||||
|
||||
Exploration clarifies your thinking *before* you spend a change on it. When the insight crystallizes, propose, and the AI carries the context forward.
|
||||
|
||||
## Recipe 4: Juggling two changes at once
|
||||
|
||||
**When to use it:** you're mid-feature and an urgent fix jumps the queue.
|
||||
|
||||
Changes are independent folders, so parallel work doesn't conflict. Start the fix, ship it, then return to the feature right where you left off.
|
||||
|
||||
```text
|
||||
You: I'm partway through add-dark-mode, but I need to fix a login bug now.
|
||||
|
||||
You: /opsx:propose fix-login-redirect
|
||||
|
||||
AI: Created openspec/changes/fix-login-redirect/. Ready for implementation.
|
||||
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Implemented the fix. All tasks complete.
|
||||
|
||||
You: /opsx:archive
|
||||
|
||||
AI: ✓ Archived fix-login-redirect.
|
||||
|
||||
You: Back to dark mode.
|
||||
|
||||
You: /opsx:apply add-dark-mode
|
||||
|
||||
AI: Resuming add-dark-mode. Picking up at task 2.3: Update Header...
|
||||
```
|
||||
|
||||
Naming the change in `/opsx:apply add-dark-mode` is how you point the AI at a specific change when more than one is active. Because tasks track completion in `tasks.md`, the AI knows exactly where you stopped.
|
||||
|
||||
When several changes are done at once, the expanded `/opsx:bulk-archive` files them together and resolves spec conflicts by checking what's actually implemented. See [Workflows](workflows.md#parallel-changes).
|
||||
|
||||
## Recipe 5: A refactor with no behavior change
|
||||
|
||||
**When to use it:** you're restructuring code, and externally visible behavior should stay identical.
|
||||
|
||||
This is the interesting case, because a pure refactor has *nothing to add to your specs*. The behavior contract doesn't change; only the implementation does. So the work lives in the design and tasks, and the spec delta is empty or absent.
|
||||
|
||||
```text
|
||||
You: /opsx:propose refactor-payment-module
|
||||
|
||||
AI: Created the change. The proposal states the goal (split the
|
||||
payment module, no behavior change) and the design captures
|
||||
the new structure. No spec changes, since behavior is identical.
|
||||
Ready for implementation.
|
||||
```
|
||||
|
||||
When you archive a change that doesn't touch specs, you can tell the terminal command to skip the spec step:
|
||||
|
||||
```bash
|
||||
$ openspec archive refactor-payment-module --skip-specs
|
||||
```
|
||||
|
||||
The same flag is handy for tooling, CI, and docs-only changes. The principle: specs describe behavior, so if behavior didn't change, the spec shouldn't either. See [Concepts](concepts.md#what-a-spec-is-and-is-not).
|
||||
|
||||
## Recipe 6: Step-by-step control (expanded commands)
|
||||
|
||||
**When to use it:** a complex or risky change where you want to review each artifact before moving on.
|
||||
|
||||
The core `/opsx:propose` drafts everything at once. When you'd rather go one step at a time, turn on the expanded commands:
|
||||
|
||||
```bash
|
||||
$ openspec config profile # select the expanded workflows
|
||||
$ openspec update # apply them to this project
|
||||
```
|
||||
|
||||
Now you can scaffold and build incrementally:
|
||||
|
||||
```text
|
||||
You: /opsx:new add-2fa
|
||||
|
||||
AI: Created openspec/changes/add-2fa/. Ready to create: proposal.
|
||||
|
||||
You: /opsx:continue
|
||||
|
||||
AI: Created proposal.md. Now available: specs, design.
|
||||
|
||||
You: /opsx:continue
|
||||
|
||||
AI: Created specs/auth/spec.md. Now available: design.
|
||||
```
|
||||
|
||||
Review each artifact as it lands, edit freely, and continue when you're happy. When you want the rest drafted in one go, `/opsx:ff` fast-forwards through the remaining planning artifacts. Before archiving, `/opsx:verify` checks that the implementation actually matches the specs. See [Workflows](workflows.md#opsxff-vs-opsxcontinue).
|
||||
|
||||
## Recipe 7: Learning the whole loop hands-on
|
||||
|
||||
**When to use it:** you've installed OpenSpec and want to *feel* the workflow on your own code, not a toy example.
|
||||
|
||||
Turn on the expanded commands (see Recipe 6), then:
|
||||
|
||||
```text
|
||||
You: /opsx:onboard
|
||||
|
||||
AI: Welcome to OpenSpec! I'll walk you through a complete change
|
||||
using your actual codebase. Let me scan for a small, safe
|
||||
improvement we can make together...
|
||||
```
|
||||
|
||||
`/opsx:onboard` finds a real (small) improvement, creates a change for it, implements it, and archives it, narrating every step. It takes 15 to 30 minutes and leaves you with a real change you can keep or discard. It's the gentlest way to learn. See [Commands](commands.md#opsxonboard).
|
||||
|
||||
## Checking your work from the terminal
|
||||
|
||||
Any time, from your terminal, you can inspect the state of things:
|
||||
|
||||
```bash
|
||||
$ openspec list # active changes
|
||||
$ openspec show add-dark-mode # one change in detail
|
||||
$ openspec validate add-dark-mode # check structure
|
||||
$ openspec view # interactive dashboard
|
||||
```
|
||||
|
||||
These are read-and-inspect tools. The proposing and building still happen through slash commands in chat. Full details in the [CLI reference](cli.md).
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Explore First](explore.md): the recommended way to start when you're unsure
|
||||
- [Workflows](workflows.md): the patterns above, with decision guidance on when to use each
|
||||
- [Commands](commands.md): every slash command in detail
|
||||
- [Getting Started](getting-started.md): the canonical first-change walkthrough
|
||||
- [Concepts](concepts.md): why the pieces fit together the way they do
|
||||
@@ -0,0 +1,134 @@
|
||||
# Using OpenSpec in an Existing Project
|
||||
|
||||
**You do not document your whole codebase to start. You write specs only for what you're about to change.** That's the single most important thing to know about adopting OpenSpec on an existing project, and it's why OpenSpec is built brownfield-first.
|
||||
|
||||
A common worry sounds like this: "My app is 80,000 lines old. Do I have to write specs for all of it before OpenSpec is useful?" No. You'd hate that, and so would we. OpenSpec grows your specs one change at a time. Your first change documents the slice it touches, the next change documents its slice, and over months your specs fill in naturally around the work you actually do.
|
||||
|
||||
This guide shows how to start on day one without boiling the ocean.
|
||||
|
||||
## The thirty-second version
|
||||
|
||||
```bash
|
||||
$ cd your-existing-project
|
||||
$ openspec init # adds openspec/ and your AI tool's commands
|
||||
```
|
||||
|
||||
Then, in your AI chat:
|
||||
|
||||
```text
|
||||
/opsx:explore # optional: have the AI read the area you'll touch
|
||||
/opsx:propose <a real, small change you actually need>
|
||||
/opsx:apply
|
||||
/opsx:archive
|
||||
```
|
||||
|
||||
Your specs now describe exactly the part of the system that change touched, and nothing more. That's correct. You're done worrying about the other 80,000 lines.
|
||||
|
||||
## Why delta-first is the whole trick
|
||||
|
||||
OpenSpec changes are written as **deltas**: `ADDED`, `MODIFIED`, `REMOVED`. A delta describes what's changing relative to current behavior, not the entire system.
|
||||
|
||||
This is exactly what brownfield work needs. You're rarely building from nothing. You're adding a field, fixing a redirect, tightening a timeout. A delta lets you specify that one change precisely without first writing a 40-page spec of everything around it.
|
||||
|
||||
So your `openspec/specs/` directory doesn't start full and complete. It starts nearly empty and accumulates. Each archived change merges its delta in. The spec for `auth/` becomes thorough only after you've made several auth changes, which is exactly when you want it thorough.
|
||||
|
||||
If you want the deeper mechanics, see [Concepts: Delta Specs](concepts.md#delta-specs).
|
||||
|
||||
## Your first change on a real codebase
|
||||
|
||||
Pick something small and real. Not a toy, not a rewrite. A change you were going to make this week anyway. Small first changes teach you the workflow with low stakes.
|
||||
|
||||
**Step 1: Let the AI read the relevant area.** This is where `/opsx:explore` earns its keep on an unfamiliar or large codebase. Point it at the part you're about to touch and let it map how things work before proposing anything.
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: I need to add rate limiting to our public API, but I'm not sure
|
||||
how requests currently flow through the middleware.
|
||||
|
||||
AI: Let me trace it... [reads the router, middleware stack, and config]
|
||||
Requests hit Express, pass through auth middleware, then your
|
||||
controllers. There's no rate-limiting layer today. The cleanest
|
||||
insertion point is a middleware right after auth. Want me to scope it?
|
||||
```
|
||||
|
||||
Notice the AI now understands your actual structure, so the proposal it writes will fit your code, not a generic template. On a big codebase, this single habit saves the most pain. See [Explore First](explore.md).
|
||||
|
||||
**Step 2: Propose the change.** The proposal and its delta spec capture just this change.
|
||||
|
||||
```text
|
||||
You: /opsx:propose add-api-rate-limiting
|
||||
```
|
||||
|
||||
**Step 3: Build and archive** with `/opsx:apply` and `/opsx:archive`, same as any change. After archiving, you have a real spec for your rate-limiting behavior, born from a change you needed anyway.
|
||||
|
||||
## Prefer a guided tour? Use onboard
|
||||
|
||||
If you'd rather watch the whole loop happen on your own code with narration, the expanded command `/opsx:onboard` does exactly that: it scans your codebase for a small, safe improvement, then walks you through proposing, building, and archiving it, explaining each step.
|
||||
|
||||
Turn on the expanded commands first:
|
||||
|
||||
```bash
|
||||
$ openspec config profile # select the expanded workflows
|
||||
$ openspec update # apply them to this project
|
||||
```
|
||||
|
||||
Then in chat:
|
||||
|
||||
```text
|
||||
/opsx:onboard
|
||||
```
|
||||
|
||||
It's the gentlest possible introduction on a real project, and it leaves you with a genuine (small) change you can keep or discard. See [Commands: `/opsx:onboard`](commands.md#opsxonboard).
|
||||
|
||||
## "But I already have requirements docs"
|
||||
|
||||
Maybe you have a PRD, an SRS, a formal spec, even TLA+ models. Good. You don't import them wholesale, and you don't throw them away either.
|
||||
|
||||
Treat existing docs as **source material for exploration**, not as specs to convert. When you start a change, paste or point the AI at the relevant section, and let it shape a focused OpenSpec delta from it. The delta captures the behavior you're changing now, in OpenSpec's testable requirement-and-scenario form. Your original documents stay where they are as background.
|
||||
|
||||
The honest reason: OpenSpec specs are deliberately behavior-first and scoped to changes. A 40-page PRD is a different artifact with a different job. Forcing a one-time bulk conversion tends to produce a large, stale spec nobody trusts. Letting specs grow from real changes keeps them accurate.
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
You: Here's the section of our PRD about checkout. I'm implementing the
|
||||
"guest checkout" requirement next.
|
||||
[paste the relevant requirement]
|
||||
AI: [reads it, asks clarifying questions, then helps scope a change]
|
||||
You: /opsx:propose add-guest-checkout
|
||||
```
|
||||
|
||||
## Organizing specs in a big codebase
|
||||
|
||||
Specs live under `openspec/specs/`, grouped by **domain**: a logical area that matches how your team thinks about the system. You don't have to design the whole taxonomy up front. Create a domain folder when your first change in that area needs one.
|
||||
|
||||
Common ways to slice domains:
|
||||
|
||||
- **By feature area:** `auth/`, `payments/`, `search/`
|
||||
- **By component:** `api/`, `frontend/`, `workers/`
|
||||
- **By bounded context:** `ordering/`, `fulfillment/`, `inventory/`
|
||||
|
||||
Pick whatever makes a newcomer nod. You can refine later. See [Concepts: Specs](concepts.md#specs).
|
||||
|
||||
## Monorepos and work that spans repos
|
||||
|
||||
For a monorepo, the simplest model is one `openspec/` directory at the repo root, with domains that map to your packages or services. That covers most teams.
|
||||
|
||||
If your work genuinely spans **multiple repositories** (or several packages you treat as separate), OpenSpec has a beta **stores** feature: planning lives in its own standalone repo that any of your code repos can reference, so the plan does not have to live inside one repo's `openspec/` folder. It's beta, so treat its commands and state as evolving. Start with the [Stores User Guide](stores-beta/user-guide.md) for the mental model and the smallest useful path.
|
||||
|
||||
## A few honest cautions
|
||||
|
||||
- **Resist the urge to back-fill everything.** Writing specs for code you aren't changing feels productive and usually isn't. Those specs go stale, because nothing forces them to track reality. Let real changes drive your specs.
|
||||
- **Keep early changes small.** Your first few changes are as much about learning the rhythm as shipping. A tight scope makes the loop fast and the lessons cheap.
|
||||
- **Commit `openspec/` to git.** Your specs and archive belong in version control alongside the code they describe.
|
||||
- **Give the AI context.** On a large codebase with strong conventions, fill in `openspec/config.yaml`'s `context:` so every proposal respects your stack and patterns. See [Customization](customization.md#project-configuration).
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Explore First](explore.md) - the key habit for understanding code before you change it
|
||||
- [Getting Started](getting-started.md) - the full first-change walkthrough
|
||||
- [Editing & Iterating on a Change](editing-changes.md) - adjusting a change as you learn
|
||||
- [Concepts: Delta Specs](concepts.md#delta-specs) - why deltas make brownfield work clean
|
||||
- [Customization](customization.md) - teach OpenSpec your project's conventions
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
# Explore First
|
||||
|
||||
**`/opsx:explore` is your thinking partner. Reach for it whenever you have a problem but not yet a plan.** It investigates your codebase, weighs options with you, and clarifies what you actually want, all before a single artifact or line of code is created. When the picture is clear, it hands off to `/opsx:propose`.
|
||||
|
||||
If you take one habit from these docs, take this one: **when you're not sure, explore before you propose.**
|
||||
|
||||
Here's why that matters. AI coding assistants are eager. Ask vaguely and they'll confidently build *something*, just maybe not the thing you needed. Explore is the cure. It's a no-stakes conversation where you and the AI figure out the right move together, so that by the time you propose, you're proposing the right thing.
|
||||
|
||||
## When to explore
|
||||
|
||||
Explore is the right first step more often than people expect. Use it when any of these is true:
|
||||
|
||||
- You know the *problem* but not the *solution*. ("Pages feel slow." "Auth is a mess." "We keep getting duplicate orders.")
|
||||
- You're choosing between approaches and want the tradeoffs laid out against your actual code.
|
||||
- You're new to a codebase and need to understand how something works before you change it.
|
||||
- The requirements are fuzzy and you want to sharpen them before committing.
|
||||
- You suspect the work is bigger or smaller than it looks and want to scope it honestly.
|
||||
|
||||
Skip explore only when you already know exactly what you want and how. In that case go straight to [`/opsx:propose`](commands.md#opsxpropose).
|
||||
|
||||
## What it does (and doesn't)
|
||||
|
||||
Explore is a **conversation**, not a generator.
|
||||
|
||||
**It does:**
|
||||
- Read and search your codebase to answer real questions.
|
||||
- Compare options and name the tradeoffs of each.
|
||||
- Draw diagrams to make a design legible.
|
||||
- Help you narrow a vague idea into a concrete, buildable scope.
|
||||
- Transition to `/opsx:propose` when you're ready.
|
||||
|
||||
**It does not:**
|
||||
- Create a change folder.
|
||||
- Write any artifacts (no proposal, specs, design, or tasks).
|
||||
- Write or modify code.
|
||||
|
||||
That's the point. Exploring costs you nothing and commits you to nothing. You can explore three dead ends, learn something from each, and only then propose the path that survived.
|
||||
|
||||
## It's already installed
|
||||
|
||||
Good news: `/opsx:explore` ships in the default **core** profile, right alongside `propose`, `apply`, `sync`, and `archive`. You don't need to enable anything. If OpenSpec is set up in your project, explore is ready in your AI chat. (As with all `/opsx:*` commands, you type it in your assistant's chat, not the terminal. See [How Commands Work](how-commands-work.md).)
|
||||
|
||||
## A full example
|
||||
|
||||
Watch how a vague worry becomes a sharp, buildable change.
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: Our checkout sometimes creates duplicate orders. I don't know why
|
||||
and I don't know how to fix it safely.
|
||||
|
||||
AI: Let me dig into the checkout path...
|
||||
[reads the order service, the payment webhook handler, and the
|
||||
client submit logic]
|
||||
|
||||
Here's what I found. There are two ways a duplicate can happen:
|
||||
|
||||
1. The client can submit twice (double-click, retry on slow network).
|
||||
Nothing debounces it.
|
||||
2. The payment webhook can fire more than once, and the handler
|
||||
creates an order each time. There's no idempotency key.
|
||||
|
||||
Cause #2 is the dangerous one: it can charge and fulfill twice.
|
||||
|
||||
Three ways to fix it, roughly in order of robustness:
|
||||
- Idempotency key on the order-creation endpoint (handles both causes)
|
||||
- Dedup on the webhook by payment-intent id (handles #2 only)
|
||||
- Client-side debounce (handles #1 only, papers over the real bug)
|
||||
|
||||
Given your code already stores the payment-intent id, the
|
||||
idempotency-key approach is a small change with the biggest payoff.
|
||||
Want me to scope that?
|
||||
|
||||
You: Yes, let's do the idempotency key.
|
||||
|
||||
You: /opsx:propose add-order-idempotency-key
|
||||
|
||||
AI: Created openspec/changes/add-order-idempotency-key/, with a proposal
|
||||
and delta spec grounded in what we just found. Ready for implementation.
|
||||
```
|
||||
|
||||
Notice what happened. The starting point was "something is wrong and I'm scared to touch it." Twenty seconds of exploration turned that into a named root cause, three ranked options, a recommendation tied to the existing code, and a precise change. The proposal that follows is sharp because the thinking happened first.
|
||||
|
||||
## Handing off to propose
|
||||
|
||||
Explore doesn't archive into anything. When you're ready, you simply start a change, and the AI carries the context from your conversation into the artifacts.
|
||||
|
||||
```text
|
||||
explore ──► propose ──► apply ──► archive
|
||||
(think) (agree) (build) (record)
|
||||
```
|
||||
|
||||
You can say it in plain language ("let's turn this into a change") or run `/opsx:propose <name>` directly. Either way, the exploration you just did becomes the foundation of the proposal, not throwaway chat.
|
||||
|
||||
If you use the expanded command set, explore can hand off to `/opsx:new` instead, for step-by-step artifact creation. See [Workflows](workflows.md).
|
||||
|
||||
## Tips for a good exploration
|
||||
|
||||
- **Bring the problem, not the solution.** "Logins feel slow" gives the AI room to investigate. "Add a Redis cache" pre-commits you to an answer you haven't tested yet.
|
||||
- **Ask for the tradeoffs out loud.** "What are the downsides of each option?" gets you a more honest comparison.
|
||||
- **Let it read first.** The best explorations start with the AI actually looking at your code, not guessing. Point it at the relevant area if it helps.
|
||||
- **It's okay to bail.** If exploration reveals the idea isn't worth it, that's a win. You learned it cheaply.
|
||||
- **Explore again mid-change.** Stuck during `/opsx:apply`? You can step back and explore a sub-problem, then return.
|
||||
|
||||
## The honest tradeoffs
|
||||
|
||||
**What you gain:** explore catches wrong turns at the cheapest possible moment, before any artifact exists. It's especially powerful in unfamiliar code, where the AI's ability to read and summarize the system saves you an afternoon of spelunking.
|
||||
|
||||
**What it costs:** a little patience. Explore is a conversation, so it's slower than firing off `/opsx:propose` and hoping. For work you genuinely understand already, that extra step is pure overhead, and you should skip it.
|
||||
|
||||
The rule of thumb: the fuzzier the task, the more explore pays off. The clearer the task, the more you can skip straight to proposing.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Commands: `/opsx:explore`](commands.md#opsxexplore): the precise reference
|
||||
- [Workflows](workflows.md): explore as part of the everyday loop
|
||||
- [Examples & Recipes](examples.md#recipe-3-exploring-before-you-commit): explore in a full walkthrough
|
||||
- [Getting Started](getting-started.md): the first-change guide, exploration included
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
# FAQ
|
||||
|
||||
Quick answers to the questions people ask most. If your question is really a "something is broken" question, [Troubleshooting](troubleshooting.md) is the better page. If you want a term defined, see the [Glossary](glossary.md).
|
||||
|
||||
## The basics
|
||||
|
||||
### What is OpenSpec, in one sentence?
|
||||
|
||||
A lightweight layer that gets you and your AI coding assistant to agree on what to build, in writing, before any code is written.
|
||||
|
||||
### Why would I want that?
|
||||
|
||||
Because AI assistants are confident even when they're wrong. When the requirements live only in a chat thread, the AI fills gaps with guesses, and you find out after the code exists. OpenSpec moves the agreement earlier, where mistakes are cheap to fix. See [Core Concepts at a Glance](overview.md) for the full case.
|
||||
|
||||
### Do I have to use it for everything?
|
||||
|
||||
No. Use it where agreement matters, which is most non-trivial work. For a one-character typo fix, the ceremony probably isn't worth it, and that's fine.
|
||||
|
||||
### Can I use it on a big existing codebase, or only new projects?
|
||||
|
||||
Existing codebases are the main event. OpenSpec is brownfield-first: you do not document your whole app up front. You write specs only for what each change touches, and your specs fill in over time around the work you actually do. There's a dedicated guide: [Using OpenSpec in an Existing Project](existing-projects.md).
|
||||
|
||||
### Is it tied to one AI tool?
|
||||
|
||||
No. OpenSpec works with 25+ assistants, including Claude Code, Cursor, Windsurf, GitHub Copilot, Gemini CLI, Codex, and more. The full list and per-tool details are in [Supported Tools](supported-tools.md).
|
||||
|
||||
## Running commands
|
||||
|
||||
### Where do I type `/opsx:propose`?
|
||||
|
||||
In your AI assistant's chat, not your terminal. This is the single most common point of confusion, so it has its own page: [How Commands Work](how-commands-work.md). Short version: `openspec ...` runs in the terminal, `/opsx:...` runs in chat.
|
||||
|
||||
### How do I "start interactive mode"?
|
||||
|
||||
There isn't a separate mode to start. You open your AI assistant like normal and type a slash command into its chat. The slash command is how you "enter" OpenSpec. (The one genuinely interactive terminal feature is `openspec view`, a dashboard for browsing specs and changes.) Full explanation in [How Commands Work](how-commands-work.md).
|
||||
|
||||
### I typed a slash command and nothing happened. Why?
|
||||
|
||||
Most likely you typed it in the terminal instead of your AI chat, or the commands aren't installed yet. Run `openspec update` in your project, restart your assistant, then try typing `/opsx` in chat and watch for autocomplete. [Troubleshooting](troubleshooting.md#commands-dont-show-up) has the full checklist.
|
||||
|
||||
### Why is the syntax `/opsx:propose` in one tool and `/opsx-propose` in another?
|
||||
|
||||
Each AI tool surfaces custom commands a little differently. The intent is identical; only the punctuation changes. Type a slash in your chat and the autocomplete shows you the form your tool expects. The per-tool table is in [How Commands Work](how-commands-work.md#slash-command-syntax-by-tool).
|
||||
|
||||
### What's the difference between a skill and a command?
|
||||
|
||||
Both are files OpenSpec writes so your assistant can run the workflow. Skills (`.../skills/openspec-*/SKILL.md`) are the newer cross-tool standard; commands (`.../commands/opsx-*`) are the older per-tool slash files. You don't need to pick. You just type the slash command, and OpenSpec installs whichever your tool uses.
|
||||
|
||||
## The workflow
|
||||
|
||||
### Where should I start if I'm not sure what to build?
|
||||
|
||||
With `/opsx:explore`. It's a no-stakes thinking partner that reads your codebase, lays out options, and turns a fuzzy problem into a concrete plan, all before any change or code exists. It's in the default profile, so it's always available. When the plan is clear, it hands off to `/opsx:propose`. This is the single best habit to form, because it stops an eager AI from confidently building the wrong thing. See [Explore First](explore.md).
|
||||
|
||||
### What's the simplest possible flow?
|
||||
|
||||
```text
|
||||
/opsx:explore (optional) then /opsx:propose <what you want> then /opsx:apply then /opsx:archive
|
||||
```
|
||||
|
||||
Explore to think it through, propose to draft the plan, apply to build it, archive to file it away. Skip explore when you already know exactly what you want.
|
||||
|
||||
### What's the difference between `/opsx:propose` and `/opsx:new`?
|
||||
|
||||
`/opsx:propose` is the default one-step command: it creates the change and drafts all the planning artifacts at once. `/opsx:new` is part of the expanded command set and only scaffolds an empty change, leaving you to create artifacts one at a time with `/opsx:continue` (or all at once with `/opsx:ff`). Use propose unless you want step-by-step control. See [Commands](commands.md).
|
||||
|
||||
### What are `core` and expanded profiles?
|
||||
|
||||
A profile decides which slash commands get installed. **Core** (the default) gives you `propose`, `explore`, `apply`, `sync`, `archive`. The **expanded** set adds `new`, `continue`, `ff`, `verify`, `bulk-archive`, and `onboard` for finer control. Switch with `openspec config profile`, then apply with `openspec update`.
|
||||
|
||||
### Do I need to run `/opsx:sync`?
|
||||
|
||||
Usually not. Sync merges a change's delta specs into your main specs, and `/opsx:archive` will offer to do it for you. Run sync manually only when you want the specs merged before archiving, for example on a long-running change. See [Commands](commands.md#opsxsync).
|
||||
|
||||
### How do I edit a proposal, spec, or task after I've started?
|
||||
|
||||
Just edit the file. Every artifact is plain Markdown in `openspec/changes/<name>/`, and there's no locked phase or special edit mode. Change it by hand, or ask your AI to revise it ("update the design to use a queue"), then keep going. The AI always works from the current file contents. Full guide: [Editing & Iterating on a Change](editing-changes.md).
|
||||
|
||||
### Can I go back and change the plan after implementing some of it?
|
||||
|
||||
Yes, at any time. The workflow is fluid, so review and editing aren't phases you get locked out of. Edit the artifact, then continue. If you want a structured check that the code still matches the plan, run `/opsx:verify`. See [Editing & Iterating on a Change](editing-changes.md#how-do-i-go-back-to-review-after-implementing).
|
||||
|
||||
### I edited the code by hand. How do I reconcile it with the spec?
|
||||
|
||||
Bring them back in sync before you archive, since archiving makes your specs the record of truth. If the code is now correct, update the delta spec to match what you shipped; if the spec is correct, keep building until the code agrees. `/opsx:verify` surfaces the mismatches. See [Editing & Iterating on a Change](editing-changes.md#i-edited-the-code-by-hand-how-do-i-reconcile-that-with-openspec).
|
||||
|
||||
### When should I update an existing change versus start a new one?
|
||||
|
||||
Update when it's the same work, refined. Start fresh when the intent fundamentally changed or the scope exploded into different work. There's a decision flowchart and examples in [Workflows](workflows.md#when-to-update-vs-start-fresh).
|
||||
|
||||
### What if my session runs out of context, or requirements change mid-implementation?
|
||||
|
||||
This is where specs earn their keep. Because the plan lives in files (not only in chat history), you can clear your context, start a fresh AI session, and pick up with `/opsx:apply`; it reads the artifacts and resumes from the first unchecked task. If requirements change, edit the artifacts to match the new reality and continue. Keeping a clean context window also produces better results; clear it before implementation.
|
||||
|
||||
### Should I commit the `openspec/` folder to git?
|
||||
|
||||
Yes. Your specs, active changes, and archive are part of your project's history. Commit them like any other source. The archive in particular becomes a durable record of why your system works the way it does.
|
||||
|
||||
## Specs and changes
|
||||
|
||||
### What goes in a spec versus a design?
|
||||
|
||||
A spec describes observable behavior: what the system does, its inputs, outputs, and error conditions. A design describes how you'll build it: the technical approach, architecture decisions, file changes. If implementation could change without changing externally visible behavior, it belongs in the design, not the spec. [Concepts](concepts.md#what-a-spec-is-and-is-not) goes deeper.
|
||||
|
||||
### What's a delta spec?
|
||||
|
||||
A spec that describes only what's changing, using `ADDED`, `MODIFIED`, and `REMOVED` sections, rather than restating the whole spec. It's how OpenSpec handles edits to existing systems cleanly. See [Concepts](concepts.md#delta-specs).
|
||||
|
||||
### Where do archived changes go?
|
||||
|
||||
To `openspec/changes/archive/YYYY-MM-DD-<name>/`, with all artifacts preserved. Nothing is deleted; the change just moves out of your active list.
|
||||
|
||||
## Configuration and customization
|
||||
|
||||
### How do I tell the AI about my tech stack?
|
||||
|
||||
Put it in `openspec/config.yaml` under `context:`. That text is injected into every planning request, so the AI always knows your stack and conventions. See [Customization](customization.md#project-configuration).
|
||||
|
||||
### Can I generate specs in a language other than English?
|
||||
|
||||
Yes. Add a language instruction to your config's `context:`. [Multi-Language](multi-language.md) has copy-paste snippets for several languages.
|
||||
|
||||
### Can I change the workflow itself?
|
||||
|
||||
Yes, with custom schemas. A schema defines which artifacts exist and how they depend on each other. Fork the default with `openspec schema fork spec-driven my-workflow`, then edit it. See [Customization](customization.md#custom-schemas).
|
||||
|
||||
## Models, privacy, and upgrades
|
||||
|
||||
### Which AI model should I use?
|
||||
|
||||
OpenSpec works best with high-reasoning models. The README recommends models like Codex 5.5 and Opus 4.7 for both planning and implementation. Also keep your context window clean: clear it before implementation for best results.
|
||||
|
||||
### Does OpenSpec collect data?
|
||||
|
||||
It collects anonymous usage stats: command names and version only. No arguments, paths, content, or personal data, and it's off automatically in CI. Opt out with `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`.
|
||||
|
||||
### How do I upgrade?
|
||||
|
||||
Two steps. Upgrade the package (`npm install -g @fission-ai/openspec@latest`), then run `openspec update` inside each project to refresh the generated skills and commands.
|
||||
|
||||
### How do I uninstall OpenSpec?
|
||||
|
||||
There's no uninstall command, because it's just a global package plus files in your project. Remove the package (`npm uninstall -g @fission-ai/openspec`), and optionally delete the `openspec/` directory and the generated tool files. Step-by-step, including what's safe to keep, is in [Installation: Uninstalling](installation.md#uninstalling).
|
||||
|
||||
## Getting help
|
||||
|
||||
### Where do I ask questions or report bugs?
|
||||
|
||||
- **Discord:** [discord.gg/YctCnvvshC](https://discord.gg/YctCnvvshC)
|
||||
- **GitHub Issues:** [github.com/Fission-AI/OpenSpec/issues](https://github.com/Fission-AI/OpenSpec/issues)
|
||||
- **From your terminal:** `openspec feedback "your message"` opens a GitHub issue for you.
|
||||
|
||||
### These docs are wrong or confusing. What do I do?
|
||||
|
||||
Tell us, or fix it. Documentation PRs are welcome and valued. Open an issue or send a pull request.
|
||||
+57
-41
@@ -1,36 +1,52 @@
|
||||
# Getting Started
|
||||
|
||||
This guide explains how OpenSpec works after you've installed and initialized it. For installation instructions, see the [main README](../README.md#quick-start).
|
||||
This guide explains how OpenSpec works after you've installed and initialized it. For installation instructions, see the [main README](../README.md#quick-start) or the [Installation guide](installation.md). New to the whole docs set? The [documentation home](README.md) maps everything.
|
||||
|
||||
> **Where do I type these commands?** Two places, and mixing them up is the most common early stumble.
|
||||
>
|
||||
> - `openspec ...` commands (like `openspec init`) run in your **terminal**.
|
||||
> - `/opsx:...` commands (like `/opsx:propose`) run in your **AI assistant's chat**, the same box where you'd ask it to write code.
|
||||
>
|
||||
> There's no separate "interactive mode" to start. You just type the slash command in chat and your assistant takes it from there. Full explanation: [How Commands Work](how-commands-work.md).
|
||||
|
||||
## Your First Five Minutes
|
||||
|
||||
The whole loop, with each step labeled by where it happens:
|
||||
|
||||
```text
|
||||
TERMINAL $ npm install -g @fission-ai/openspec@latest
|
||||
TERMINAL $ cd your-project && openspec init
|
||||
AI CHAT /opsx:explore (optional: think it through first)
|
||||
AI CHAT /opsx:propose add-dark-mode (AI drafts the plan; you review it)
|
||||
AI CHAT /opsx:apply (AI builds it)
|
||||
AI CHAT /opsx:archive (specs updated, change filed away)
|
||||
```
|
||||
|
||||
Two terminal steps to set up, then you live in chat. The rest of this guide unpacks what each step does and what you'll see.
|
||||
|
||||
> **Not sure what to build yet? Start with `/opsx:explore`.** It's a no-stakes thinking partner that reads your codebase, weighs options, and sharpens a fuzzy idea into a concrete plan, all before any artifact or code exists. When the picture is clear, it hands off to `/opsx:propose`. This is the single best habit for working with an AI that will otherwise confidently build the wrong thing. See the [Explore guide](explore.md).
|
||||
|
||||
## How It Works
|
||||
|
||||
OpenSpec helps you and your AI coding assistant agree on what to build before any code is written. The workflow follows a simple pattern:
|
||||
OpenSpec helps you and your AI coding assistant agree on what to build before any code is written.
|
||||
|
||||
**Default quick path (core profile):**
|
||||
|
||||
```text
|
||||
/opsx:explore ──► /opsx:propose ──► /opsx:apply ──► /opsx:sync ──► /opsx:archive
|
||||
(optional)
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ Start a Change │ /opsx:new
|
||||
└────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Create Artifacts │ /opsx:ff or /opsx:continue
|
||||
│ (proposal, specs, │
|
||||
│ design, tasks) │
|
||||
└────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Implement Tasks │ /opsx:apply
|
||||
│ (AI writes code) │
|
||||
└────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Archive & Merge │ /opsx:archive
|
||||
│ Specs │
|
||||
└────────────────────┘
|
||||
|
||||
Start with `/opsx:explore` when you're figuring out what to do, or jump straight to `/opsx:propose` when you already know. Explore is in the default profile, so it's always there when you want it.
|
||||
|
||||
**Expanded path (custom workflow selection):**
|
||||
|
||||
```text
|
||||
/opsx:new ──► /opsx:ff or /opsx:continue ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive
|
||||
```
|
||||
|
||||
The default global profile is `core`, which includes `propose`, `explore`, `apply`, `sync`, and `archive`. You can enable the expanded workflow commands with `openspec config profile` and then `openspec update`.
|
||||
|
||||
## What OpenSpec Creates
|
||||
|
||||
After running `openspec init`, your project has this structure:
|
||||
@@ -131,23 +147,12 @@ The change folder moves to `openspec/changes/archive/` for audit history.
|
||||
|
||||
Let's walk through adding dark mode to an application.
|
||||
|
||||
### 1. Start the Change
|
||||
### 1. Start the Change (Default)
|
||||
|
||||
```
|
||||
You: /opsx:new add-dark-mode
|
||||
```text
|
||||
You: /opsx:propose add-dark-mode
|
||||
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
Ready to create: proposal
|
||||
```
|
||||
|
||||
### 2. Create Artifacts
|
||||
|
||||
Use `/opsx:ff` (fast-forward) to create all planning artifacts at once:
|
||||
|
||||
```
|
||||
You: /opsx:ff
|
||||
|
||||
AI: Creating artifacts for add-dark-mode...
|
||||
✓ proposal.md — why we're doing this, what's changing
|
||||
✓ specs/ — requirements and scenarios
|
||||
✓ design.md — technical approach
|
||||
@@ -155,7 +160,9 @@ AI: Creating artifacts for add-dark-mode...
|
||||
Ready for implementation!
|
||||
```
|
||||
|
||||
### 3. What Gets Created
|
||||
If you've enabled the expanded workflow profile, you can also do this as two steps: `/opsx:new` then `/opsx:ff` (or `/opsx:continue` incrementally).
|
||||
|
||||
### 2. What Gets Created
|
||||
|
||||
**proposal.md** - Captures the intent:
|
||||
|
||||
@@ -218,7 +225,7 @@ The system SHALL allow users to choose between light and dark themes.
|
||||
- [ ] 3.2 Update components to use CSS variables
|
||||
```
|
||||
|
||||
### 4. Implement
|
||||
### 3. Implement
|
||||
|
||||
```
|
||||
You: /opsx:apply
|
||||
@@ -234,7 +241,7 @@ AI: Working through tasks...
|
||||
|
||||
During implementation, if you discover the design needs adjustment, just update the artifact and continue.
|
||||
|
||||
### 5. Archive
|
||||
### 4. Archive
|
||||
|
||||
```
|
||||
You: /opsx:archive
|
||||
@@ -267,7 +274,16 @@ openspec view
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Explore First](explore.md) - Use `/opsx:explore` to think through an idea before you commit
|
||||
- [Reviewing a Change](reviewing-changes.md) - What to check in the plan the AI drafts, before any code
|
||||
- [Writing Good Specs](writing-specs.md) - What a strong requirement and scenario look like
|
||||
- [Using OpenSpec in an Existing Project](existing-projects.md) - Start on a large brownfield codebase
|
||||
- [Editing & Iterating on a Change](editing-changes.md) - Update artifacts, go back, reconcile manual edits
|
||||
- [Core Concepts at a Glance](overview.md) - The whole mental model on one page
|
||||
- [Examples & Recipes](examples.md) - Real changes, start to finish
|
||||
- [Workflows](workflows.md) - Common patterns and when to use each command
|
||||
- [Commands](commands.md) - Full reference for all slash commands
|
||||
- [Concepts](concepts.md) - Deeper understanding of specs, changes, and schemas
|
||||
- [Customization](customization.md) - Make OpenSpec work your way
|
||||
- [Stores](stores-beta/user-guide.md) - Planning that spans repos or teams? Keep it in its own repo (beta)
|
||||
- [FAQ](faq.md) and [Troubleshooting](troubleshooting.md) - When you get stuck
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Glossary
|
||||
|
||||
Every OpenSpec term in one place, defined in plain language. Skim it once and the rest of the docs read faster.
|
||||
|
||||
Terms are grouped by topic, then alphabetized within each group.
|
||||
|
||||
## The core nouns
|
||||
|
||||
**Spec.** A document describing how part of your system behaves. Specs live in `openspec/specs/`, are organized by domain, and are made of requirements and scenarios. The spec is the agreed-upon answer to "what does this software do?" See [Concepts](concepts.md#specs).
|
||||
|
||||
**Source of truth.** The `openspec/specs/` directory as a whole. It holds the current, agreed-upon behavior of your system. Changes propose edits to it; archiving applies them.
|
||||
|
||||
**Change.** One unit of work, packaged as a folder under `openspec/changes/<name>/`. A change holds everything about that work: its proposal, design, tasks, and the spec edits it introduces. One change, one feature or fix.
|
||||
|
||||
**Artifact.** A document inside a change. The standard artifacts are the proposal, the delta specs, the design, and the tasks. They're created in dependency order and feed into each other.
|
||||
|
||||
**Delta spec.** A spec inside a change that describes only what's changing, using `ADDED`, `MODIFIED`, and `REMOVED` sections, rather than restating the entire spec. This is what lets OpenSpec edit existing systems cleanly. See [Concepts](concepts.md#delta-specs).
|
||||
|
||||
**Domain.** A logical grouping for specs, like `auth/`, `payments/`, or `ui/`. You choose domains that match how you think about your system.
|
||||
|
||||
## Inside a spec
|
||||
|
||||
**Requirement.** A single behavior the system must have, usually written with an RFC 2119 keyword: "The system SHALL expire sessions after 30 minutes." Requirements state the *what*, not the *how*.
|
||||
|
||||
**Scenario.** A concrete, testable example of a requirement in action, typically in Given/When/Then form. Scenarios make a requirement verifiable: you could write an automated test from one.
|
||||
|
||||
**RFC 2119 keywords.** The words MUST, SHALL, SHOULD, and MAY, which carry standardized meaning about how strict a requirement is. MUST and SHALL are absolute. SHOULD is recommended with room for exceptions. MAY is optional. The name comes from the internet standards document that defined them.
|
||||
|
||||
## The artifacts
|
||||
|
||||
**Proposal (`proposal.md`).** The *why* and *what* of a change: its intent, scope, and high-level approach. The first artifact you create.
|
||||
|
||||
**Design (`design.md`).** The *how*: technical approach, architecture decisions, and the files you expect to touch. Optional for simple changes.
|
||||
|
||||
**Tasks (`tasks.md`).** The implementation checklist, with checkboxes. The AI works through it during `/opsx:apply` and checks items off as it goes.
|
||||
|
||||
## The lifecycle
|
||||
|
||||
**Archive.** The act of finishing a change. Its delta specs merge into the main specs, and the change folder moves to `openspec/changes/archive/YYYY-MM-DD-<name>/`. After archiving, your specs describe the new reality. See [Concepts](concepts.md#archive).
|
||||
|
||||
**Sync.** Merging a change's delta specs into the main specs *without* archiving the change. Usually automatic (archive offers to do it), but available on its own as `/opsx:sync` for long-running changes. See [Commands](commands.md#opsxsync).
|
||||
|
||||
## Workflow and commands
|
||||
|
||||
**OPSX.** The current standard OpenSpec workflow, built around fluid actions instead of rigid phases. Its slash commands all start with `/opsx:`. See [OPSX Workflow](opsx.md).
|
||||
|
||||
**Slash command.** A command you type into your AI assistant's chat, like `/opsx:propose`. Slash commands drive the workflow. They are not terminal commands. See [How Commands Work](how-commands-work.md).
|
||||
|
||||
**Explore (`/opsx:explore`).** The thinking-partner command. It reads your codebase, compares options, and clarifies a fuzzy idea into a concrete plan, creating no artifacts and writing no code. The recommended starting point whenever you have a problem but not yet a plan. See [Explore First](explore.md).
|
||||
|
||||
**CLI.** The `openspec` program you run in your terminal. It sets up projects, lists and validates changes, opens the dashboard, and archives. The terminal half of OpenSpec. See [CLI](cli.md).
|
||||
|
||||
**Skill.** A folder of instructions (`.../skills/openspec-*/SKILL.md`) that your AI assistant auto-detects and follows. Skills are the emerging cross-tool standard for delivering the OpenSpec workflow to your assistant.
|
||||
|
||||
**Command file.** A per-tool slash command file (`.../commands/opsx-*`). The older delivery mechanism, still supported alongside skills. You rarely touch these directly.
|
||||
|
||||
**Profile.** The set of slash commands installed in your project. **Core** (the default) is `propose`, `explore`, `apply`, `sync`, `archive`. The **expanded** set adds `new`, `continue`, `ff`, `verify`, `bulk-archive`, `onboard`. Change it with `openspec config profile`.
|
||||
|
||||
**Delivery.** Whether OpenSpec installs skills, command files, or both for your tools. Configured globally and applied with `openspec update`.
|
||||
|
||||
## Customization
|
||||
|
||||
**Schema.** The definition of which artifacts a workflow has and how they depend on one another. The built-in default is `spec-driven` (proposal → specs → design → tasks). You can fork it or write your own. See [Customization](customization.md#custom-schemas).
|
||||
|
||||
**Template.** A Markdown file inside a schema that shapes what the AI generates for a given artifact. Editing a template changes the AI's output immediately, with no rebuild.
|
||||
|
||||
**Project config (`openspec/config.yaml`).** Per-project settings: the default schema, the `context:` injected into every planning request, and per-artifact `rules:`. The easiest way to teach OpenSpec about your stack and conventions. See [Customization](customization.md#project-configuration).
|
||||
|
||||
**Context injection.** Putting project background in `config.yaml`'s `context:` field so it's automatically added to every artifact the AI generates. More reliable than hoping the AI reads a separate file.
|
||||
|
||||
**Dependency graph.** The directed graph formed by artifact `requires:` relationships. It's a DAG (directed acyclic graph: arrows only point forward, never in a loop), and OpenSpec uses it to know what you can create next.
|
||||
|
||||
**Enablers, not gates.** The principle that artifact dependencies show what becomes *possible* next, not what's *required* next. You can revisit and edit any artifact at any time. See [Core Concepts at a Glance](overview.md#enablers-not-gates).
|
||||
|
||||
## Coordination across repos (beta)
|
||||
|
||||
These terms apply only if your planning spans more than one repo. They're in beta. Most users can ignore them. See the [Stores User Guide](stores-beta/user-guide.md).
|
||||
|
||||
**Store.** A standalone repo whose whole job is planning. It has the same `openspec/` shape you already know (specs and changes) plus a small identity file. You register it on your machine once, by name, and then any OpenSpec command can work in it from anywhere.
|
||||
|
||||
**Reference.** A declaration, in a code repo's `openspec/config.yaml`, of a store that repo draws on. References are read-only: the repo keeps its own root, and `openspec instructions` gains an index of the referenced store's specs, each with the exact command to fetch it.
|
||||
|
||||
**Working context.** What `openspec context` assembles for the current repo: its OpenSpec root plus every store it references, each with how to fetch it. The answer to "what am I working with?"
|
||||
|
||||
**Workset.** A personal, machine-local set of folders you open together (a store alongside the code repos you work on). Created explicitly with `openspec workset create`; nothing about those local paths is committed to the shared planning repo.
|
||||
|
||||
## See also
|
||||
|
||||
- [Core Concepts at a Glance](overview.md): the five ideas, on one page
|
||||
- [Concepts](concepts.md): the long-form explanation
|
||||
- [How Commands Work](how-commands-work.md): slash commands versus the CLI
|
||||
@@ -0,0 +1,160 @@
|
||||
# How Commands Work
|
||||
|
||||
**The one thing to know: OpenSpec has two kinds of commands, and they run in two different places.**
|
||||
|
||||
- `openspec ...` commands run in your **terminal**. (Example: `openspec init`.)
|
||||
- `/opsx:...` commands run in your **AI assistant's chat**. (Example: `/opsx:propose`.)
|
||||
|
||||
If you ever type `/opsx:propose` into your terminal and nothing happens, this page is why. You are talking to the wrong half of OpenSpec. Slash commands are not terminal commands. They are instructions you give to your AI coding assistant, in the same chat box where you'd normally type "add a login form."
|
||||
|
||||
That single distinction is the most common stumbling block for new users, so let's make it crystal clear.
|
||||
|
||||
## The two halves
|
||||
|
||||
OpenSpec is one project wearing two hats.
|
||||
|
||||
**The CLI (terminal half).** A program named `openspec` that you install and run from your shell. It sets up your project, lists and validates changes, shows a dashboard, and archives finished work. You type these into iTerm, the VS Code terminal, PowerShell, anywhere you'd run `git` or `npm`.
|
||||
|
||||
```bash
|
||||
openspec init # set up OpenSpec in this project
|
||||
openspec list # see active changes
|
||||
openspec view # open the interactive dashboard
|
||||
```
|
||||
|
||||
**The slash commands (chat half).** Short commands like `/opsx:propose` and `/opsx:apply` that you type into your AI assistant. These tell the AI to follow the OpenSpec workflow: draft a proposal, write specs, build from the task list, archive when done. You type these into Claude Code, Cursor, Windsurf, Copilot, or whichever assistant you use.
|
||||
|
||||
```text
|
||||
/opsx:propose add-dark-mode (typed in your AI chat)
|
||||
/opsx:apply (typed in your AI chat)
|
||||
/opsx:archive (typed in your AI chat)
|
||||
```
|
||||
|
||||
Here's the mental model in one picture:
|
||||
|
||||
```text
|
||||
YOUR TERMINAL YOUR AI ASSISTANT'S CHAT
|
||||
┌──────────────────────┐ ┌──────────────────────────────┐
|
||||
│ $ openspec init │ installs │ /opsx:propose add-dark-mode │
|
||||
│ $ openspec list │ ──────────► │ /opsx:apply │
|
||||
│ $ openspec view │ commands │ /opsx:archive │
|
||||
└──────────────────────┘ & skills └──────────────────────────────┘
|
||||
run openspec here run /opsx:* here
|
||||
```
|
||||
|
||||
Notice the arrow. Running `openspec init` in your terminal is what *installs* the slash commands into your AI tool. The terminal half sets up the chat half. After that, day-to-day driving mostly happens in chat.
|
||||
|
||||
## "How do I start interactive mode?"
|
||||
|
||||
**There is no separate interactive mode to start.** This question comes up a lot, so it deserves a plain answer.
|
||||
|
||||
You don't enter a special OpenSpec mode. You just open your AI coding assistant like you always do, and type a slash command into the chat. The slash command *is* how you "enter" OpenSpec. Your assistant recognizes it, loads the matching OpenSpec skill, and starts following the workflow.
|
||||
|
||||
So the real instructions are:
|
||||
|
||||
1. Open your AI coding assistant (Claude Code, Cursor, Windsurf, and so on) in your project.
|
||||
2. Type `/opsx:propose` in its chat, the same place you type any other request.
|
||||
3. Watch the autocomplete: if OpenSpec is installed, you'll see `/opsx:propose`, `/opsx:apply`, and friends appear as you type the slash.
|
||||
|
||||
That's it. No mode to toggle, no daemon to launch, no separate window.
|
||||
|
||||
One thing that *is* genuinely interactive lives in the terminal: `openspec view`. It opens a dashboard for browsing your specs and changes. But that's a viewer, not the thing you propose and build with. The building happens through slash commands in chat.
|
||||
|
||||
## Why this split exists
|
||||
|
||||
It's worth understanding, because it explains why OpenSpec works with 25+ different AI tools.
|
||||
|
||||
The CLI is the **engine**. It knows the rules: what a change folder looks like, which artifacts depend on which, how to merge a delta spec into your source of truth. It's the same everywhere.
|
||||
|
||||
The slash commands are the **steering wheel**, and every AI tool has a slightly different one. Claude Code calls them commands. Cursor and Windsurf have their own formats. Some tools call them skills. When you run `openspec init`, OpenSpec generates the right kind of file for each tool you selected, so the same `/opsx:propose` intent works no matter which assistant you prefer.
|
||||
|
||||
The strength of this design: you learn the workflow once and carry it across tools. The tradeoff: the exact syntax of a command can differ slightly between tools, which is the next section.
|
||||
|
||||
## Slash command syntax by tool
|
||||
|
||||
The intent is identical everywhere. The punctuation differs. Use the form that matches your assistant.
|
||||
|
||||
| Tool | How you type it |
|
||||
|------|-----------------|
|
||||
| Claude Code | `/opsx:propose`, `/opsx:apply` |
|
||||
| Cursor | `/opsx-propose`, `/opsx-apply` |
|
||||
| Windsurf | `/opsx-propose`, `/opsx-apply` |
|
||||
| GitHub Copilot (IDE) | `/opsx-propose`, `/opsx-apply` |
|
||||
| Oh My Pi | `/opsx-propose`, `/opsx-apply` |
|
||||
| Kimi CLI | skill-style, e.g. `/skill:openspec-propose` |
|
||||
| Trae | `/opsx-propose`, `/opsx-apply` |
|
||||
|
||||
Most tools use either the colon form (`/opsx:propose`) or the dash form (`/opsx-propose`). A few tools surface OpenSpec as named skills instead of slash commands; for those you invoke the skill by name. The full per-tool list, including exactly which files get written where, lives in [Supported Tools](supported-tools.md).
|
||||
|
||||
When in doubt, type a slash in your AI chat and look at the autocomplete. Your tool will show you the form it expects.
|
||||
|
||||
## How the commands got there: skills and commands
|
||||
|
||||
When you run `openspec init` (or `openspec update`), OpenSpec writes small files into your project so your AI tool can find the workflow. Depending on your tool and settings, these are **skills**, **commands**, or both.
|
||||
|
||||
- **Skills** live in places like `.claude/skills/openspec-*/SKILL.md`. They're the emerging cross-tool standard: a folder of instructions your assistant auto-detects.
|
||||
- **Commands** live in places like `.claude/commands/opsx/<id>.md`. They're the older per-tool slash command files.
|
||||
|
||||
You don't have to care which one your tool uses. You just type the slash command and it works. But knowing these files exist helps when something goes wrong: if your commands vanish, it usually means these files are missing or stale, and `openspec update` regenerates them.
|
||||
|
||||
See [Supported Tools](supported-tools.md) for the exact paths per tool, and [Migration Guide](migration-guide.md) for how skills replaced the older command-only approach.
|
||||
|
||||
## Confirming it's installed
|
||||
|
||||
Quick checks, fastest first:
|
||||
|
||||
1. **Type a slash in your AI chat.** Start typing `/opsx` and watch for autocomplete suggestions. If they appear, you're set.
|
||||
2. **Look for the files.** For Claude Code, check that `.claude/skills/` contains `openspec-*` folders. Other tools use their own directories ([Supported Tools](supported-tools.md) lists them).
|
||||
3. **Re-run setup.** From your project root, run `openspec update`. This regenerates the skill and command files for whatever tools you configured.
|
||||
4. **Restart your assistant.** Many tools scan for skills and commands at startup, so a fresh window can be the missing step.
|
||||
|
||||
## Which commands do I even have?
|
||||
|
||||
By default, OpenSpec installs the **core** set of slash commands:
|
||||
|
||||
- `/opsx:explore`: think through an idea with the AI before committing to a change (great first step when you're unsure)
|
||||
- `/opsx:propose`: create a change and draft all its planning artifacts in one step
|
||||
- `/opsx:apply`: build the change by working through its task list
|
||||
- `/opsx:sync`: merge a change's spec updates into your main specs (usually automatic)
|
||||
- `/opsx:archive`: finish a change and file it away
|
||||
|
||||
A good default rhythm: `explore` when you're figuring out what to do, then `propose`, `apply`, `archive`. The [Explore First](explore.md) guide explains why that opening step pays off.
|
||||
|
||||
There's also an **expanded** set for people who want finer control (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`). You turn it on with `openspec config profile`, then apply it with `openspec update`.
|
||||
|
||||
New to all of this? `/opsx:onboard` (in the expanded set) walks you through a complete change on your own codebase, narrating each step. It's the friendliest possible introduction.
|
||||
|
||||
For what each command does in detail, see [Commands](commands.md). For when to reach for which, see [Workflows](workflows.md).
|
||||
|
||||
## A clean first run
|
||||
|
||||
Putting it together, here is the whole sequence with each step labeled by where it happens.
|
||||
|
||||
```text
|
||||
TERMINAL $ npm install -g @fission-ai/openspec@latest
|
||||
TERMINAL $ cd your-project
|
||||
TERMINAL $ openspec init
|
||||
(installs slash commands into your AI tool)
|
||||
|
||||
AI CHAT /opsx:explore
|
||||
(optional: think the idea through with the AI first)
|
||||
|
||||
AI CHAT /opsx:propose add-dark-mode
|
||||
(AI drafts proposal, specs, design, tasks)
|
||||
|
||||
AI CHAT /opsx:apply
|
||||
(AI builds it, checking off tasks)
|
||||
|
||||
AI CHAT /opsx:archive
|
||||
(change is merged into your specs and filed away)
|
||||
```
|
||||
|
||||
Two terminal steps to set up. Then you live in chat. That's the rhythm.
|
||||
|
||||
## Related
|
||||
|
||||
- [Getting Started](getting-started.md): the full first-change walkthrough
|
||||
- [Commands](commands.md): every slash command in detail
|
||||
- [CLI](cli.md): every terminal command in detail
|
||||
- [Supported Tools](supported-tools.md): per-tool syntax and file locations
|
||||
- [FAQ](faq.md): more quick answers
|
||||
- [Troubleshooting](troubleshooting.md): fixes when commands don't show up
|
||||
@@ -26,6 +26,9 @@ yarn global add @fission-ai/openspec@latest
|
||||
|
||||
### bun
|
||||
|
||||
Bun can install OpenSpec globally, but OpenSpec currently runs on Node.js.
|
||||
You still need Node.js 20.19.0 or higher available on `PATH`.
|
||||
|
||||
```bash
|
||||
bun add -g @fission-ai/openspec@latest
|
||||
```
|
||||
@@ -67,6 +70,39 @@ Or add to your development environment in `flake.nix`:
|
||||
openspec --version
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
Upgrade the package, then refresh each project's generated files:
|
||||
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest # or pnpm/yarn/bun equivalent
|
||||
openspec update # run inside each project
|
||||
```
|
||||
|
||||
`openspec update` regenerates the skill and command files for the tools you've configured, so your slash commands stay current with the installed version.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
There's no `openspec uninstall` command, because OpenSpec is just a global package plus some files in your project. Removing it is a few manual steps, and nothing here touches your source code.
|
||||
|
||||
**1. Remove the global package:**
|
||||
|
||||
```bash
|
||||
npm uninstall -g @fission-ai/openspec # or: pnpm rm -g / yarn global remove / bun rm -g
|
||||
```
|
||||
|
||||
**2. Remove OpenSpec from a project (optional).** Delete the `openspec/` directory if you no longer want its specs and changes:
|
||||
|
||||
```bash
|
||||
rm -rf openspec/
|
||||
```
|
||||
|
||||
Think before you do this: `openspec/specs/` and `openspec/changes/archive/` are your record of how the system behaves and why it changed. If you might want that history, keep the folder (or keep it in git) even after uninstalling.
|
||||
|
||||
**3. Remove generated AI tool files (optional).** OpenSpec writes skill and command files into per-tool directories like `.claude/skills/openspec-*/`, `.cursor/commands/opsx-*`, and so on. Delete the `openspec-*` skills and `opsx-*` commands for whichever tools you configured. The exact paths per tool are listed in [Supported Tools](supported-tools.md).
|
||||
|
||||
If you also have OpenSpec marker blocks in files like `CLAUDE.md` or `AGENTS.md`, remove those blocks by hand; your own content in those files is yours to keep.
|
||||
|
||||
## Next Steps
|
||||
|
||||
After installing, initialize OpenSpec in your project:
|
||||
|
||||
+36
-15
@@ -8,7 +8,7 @@ OPSX replaces the old phase-locked workflow with a fluid, action-based approach.
|
||||
|
||||
| Aspect | Legacy | OPSX |
|
||||
|--------|--------|------|
|
||||
| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | `/opsx:new`, `/opsx:continue`, `/opsx:apply`, and more |
|
||||
| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | Default: `/opsx:propose`, `/opsx:apply`, `/opsx:sync`, `/opsx:archive` (expanded workflow commands optional) |
|
||||
| **Workflow** | Create all artifacts at once | Create incrementally or all at once—your choice |
|
||||
| **Going back** | Awkward phase gates | Natural—update any artifact anytime |
|
||||
| **Customization** | Fixed structure | Schema-driven, fully hackable |
|
||||
@@ -84,6 +84,9 @@ Don't worry about getting it perfect. We're still learning what works best here,
|
||||
|
||||
Both `openspec init` and `openspec update` detect legacy files and guide you through the same cleanup process. Use whichever fits your situation:
|
||||
|
||||
- New installs default to profile `core` (`propose`, `explore`, `apply`, `sync`, `archive`).
|
||||
- Migrated installs preserve your previously installed workflows by writing a `custom` profile when needed.
|
||||
|
||||
### Using `openspec init`
|
||||
|
||||
Run this if you want to add new tools or reconfigure which tools are set up:
|
||||
@@ -141,7 +144,7 @@ Run this if you just want to migrate and refresh your existing tools to the late
|
||||
openspec update
|
||||
```
|
||||
|
||||
The update command also detects and cleans up legacy artifacts, then refreshes your skills to the latest version.
|
||||
The update command also detects and cleans up legacy artifacts, then refreshes generated skills/commands to match your current profile and delivery settings.
|
||||
|
||||
### Non-Interactive / CI Environments
|
||||
|
||||
@@ -275,30 +278,43 @@ The AI will help you identify what's essential vs. what can be trimmed.
|
||||
|
||||
## The New Commands
|
||||
|
||||
After migration, you have 9 OPSX commands instead of 3:
|
||||
Command availability is profile-dependent:
|
||||
|
||||
**Default (`core` profile):**
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/opsx:propose` | Create a change and generate planning artifacts in one step |
|
||||
| `/opsx:explore` | Think through ideas with no structure |
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create the next artifact (one at a time) |
|
||||
| `/opsx:ff` | Fast-forward—create all planning artifacts at once |
|
||||
| `/opsx:apply` | Implement tasks from tasks.md |
|
||||
| `/opsx:verify` | Validate implementation matches specs |
|
||||
| `/opsx:sync` | Preview spec merge (optional—archive prompts if needed) |
|
||||
| `/opsx:archive` | Finalize and archive the change |
|
||||
|
||||
**Expanded workflow (custom selection):**
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/opsx:new` | Start a new change scaffold |
|
||||
| `/opsx:continue` | Create the next artifact (one at a time) |
|
||||
| `/opsx:ff` | Fast-forward—create planning artifacts at once |
|
||||
| `/opsx:verify` | Validate implementation matches specs |
|
||||
| `/opsx:sync` | Merge delta specs into main specs |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes at once |
|
||||
| `/opsx:onboard` | Guided end-to-end onboarding workflow |
|
||||
|
||||
Enable expanded commands with `openspec config profile`, then run `openspec update`.
|
||||
|
||||
### Command Mapping from Legacy
|
||||
|
||||
| Legacy | OPSX Equivalent |
|
||||
|--------|-----------------|
|
||||
| `/openspec:proposal` | `/opsx:new` then `/opsx:ff` |
|
||||
| `/openspec:proposal` | `/opsx:propose` (default) or `/opsx:new` then `/opsx:ff` (expanded) |
|
||||
| `/openspec:apply` | `/opsx:apply` |
|
||||
| `/openspec:archive` | `/opsx:archive` |
|
||||
|
||||
### New Capabilities
|
||||
|
||||
These capabilities are part of the expanded workflow command set.
|
||||
|
||||
**Granular artifact creation:**
|
||||
```
|
||||
/opsx:continue
|
||||
@@ -542,9 +558,11 @@ project/
|
||||
│ └── config.yaml # NEW: Project configuration
|
||||
├── .claude/
|
||||
│ └── skills/ # NEW: OPSX skills
|
||||
│ ├── openspec-propose/ # default core profile
|
||||
│ ├── openspec-explore/
|
||||
│ ├── openspec-new-change/
|
||||
│ └── ...
|
||||
│ ├── openspec-apply-change/
|
||||
│ ├── openspec-sync-specs/
|
||||
│ └── ... # expanded profile adds new/continue/ff/etc.
|
||||
├── CLAUDE.md # OpenSpec markers removed, your content preserved
|
||||
└── AGENTS.md # OpenSpec markers removed, your content preserved
|
||||
```
|
||||
@@ -558,12 +576,15 @@ project/
|
||||
|
||||
### Command Cheatsheet
|
||||
|
||||
```
|
||||
/opsx:new Start a change
|
||||
/opsx:continue Create next artifact
|
||||
/opsx:ff Create all planning artifacts
|
||||
```text
|
||||
/opsx:propose Start quickly (default core profile)
|
||||
/opsx:apply Implement tasks
|
||||
/opsx:archive Finish and archive
|
||||
|
||||
# Expanded workflow (if enabled):
|
||||
/opsx:new Scaffold a change
|
||||
/opsx:continue Create next artifact
|
||||
/opsx:ff Create planning artifacts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
+31
-9
@@ -65,6 +65,8 @@ openspec init
|
||||
|
||||
This creates skills in `.claude/skills/` (or equivalent) that AI coding assistants auto-detect.
|
||||
|
||||
By default, OpenSpec uses the `core` workflow profile (`propose`, `explore`, `apply`, `sync`, `archive`). If you want the expanded workflow commands (`new`, `continue`, `ff`, `verify`, `bulk-archive`, `onboard`), configure them with `openspec config profile` and apply with `openspec update`.
|
||||
|
||||
During setup, you'll be prompted to create a **project config** (`openspec/config.yaml`). This is optional but recommended.
|
||||
|
||||
## Project Configuration
|
||||
@@ -155,13 +157,18 @@ rules:
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:propose` | Create a change and generate planning artifacts in one step (default quick path) |
|
||||
| `/opsx:explore` | Think through ideas, investigate problems, clarify requirements |
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create the next artifact (based on what's ready) |
|
||||
| `/opsx:ff` | Fast-forward — create all planning artifacts at once |
|
||||
| `/opsx:new` | Start a new change scaffold (expanded workflow) |
|
||||
| `/opsx:continue` | Create the next artifact (expanded workflow) |
|
||||
| `/opsx:ff` | Fast-forward planning artifacts (expanded workflow) |
|
||||
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
|
||||
| `/opsx:sync` | Sync delta specs to main (optional—archive prompts if needed) |
|
||||
| `/opsx:update` | Revise a change's planning artifacts and keep them coherent |
|
||||
| `/opsx:verify` | Validate implementation against artifacts (expanded workflow) |
|
||||
| `/opsx:sync` | Sync delta specs to main (default workflow, optional) |
|
||||
| `/opsx:archive` | Archive when done |
|
||||
| `/opsx:bulk-archive` | Archive multiple completed changes (expanded workflow) |
|
||||
| `/opsx:onboard` | Guided walkthrough of an end-to-end change (expanded workflow) |
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -169,13 +176,21 @@ rules:
|
||||
```
|
||||
/opsx:explore
|
||||
```
|
||||
Think through ideas, investigate problems, compare options. No structure required - just a thinking partner. When insights crystallize, transition to `/opsx:new` or `/opsx:ff`.
|
||||
Think through ideas, investigate problems, compare options. No structure required - just a thinking partner. When insights crystallize, transition to `/opsx:propose` (default) or `/opsx:new`/`/opsx:ff` (expanded).
|
||||
|
||||
### Start a new change
|
||||
```
|
||||
/opsx:new
|
||||
/opsx:propose
|
||||
```
|
||||
Creates the change and generates planning artifacts needed before implementation.
|
||||
|
||||
If you've enabled expanded workflows, you can instead use:
|
||||
|
||||
```text
|
||||
/opsx:new # scaffold only
|
||||
/opsx:continue # create one artifact at a time
|
||||
/opsx:ff # create all planning artifacts at once
|
||||
```
|
||||
You'll be asked what you want to build and which workflow schema to use.
|
||||
|
||||
### Create artifacts
|
||||
```
|
||||
@@ -194,6 +209,12 @@ Creates all planning artifacts at once. Use when you have a clear picture of wha
|
||||
```
|
||||
Works through tasks, checking them off as you go. If you're juggling multiple changes, you can run `/opsx:apply <name>`; otherwise it should infer from the conversation and prompt you to choose if it can't tell.
|
||||
|
||||
### Updating a change
|
||||
```
|
||||
/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).
|
||||
|
||||
### Finish up
|
||||
```
|
||||
/opsx:archive # Move to archive when done (prompts to sync specs if needed)
|
||||
@@ -299,6 +320,7 @@ Think of it like git branches:
|
||||
## Architecture Deep Dive
|
||||
|
||||
This section explains how OPSX works under the hood and how it compares to the legacy workflow.
|
||||
Examples in this section use the expanded command set (`new`, `continue`, etc.); default `core` users can map the same flow to `propose → apply → sync → archive`.
|
||||
|
||||
### Philosophy: Phases vs Actions
|
||||
|
||||
@@ -356,7 +378,7 @@ This section explains how OPSX works under the hood and how it compares to the l
|
||||
│ Hardcoded Templates (TypeScript strings) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Configurators (18+ classes, one per editor) │
|
||||
│ Tool-specific configurators/adapters │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Generated Command Files (.claude/commands/openspec/*.md) │
|
||||
@@ -604,7 +626,7 @@ artifacts:
|
||||
| **State** | Phase-based mental model | Filesystem existence |
|
||||
| **Customization** | Edit source, rebuild | Create schema.yaml |
|
||||
| **Iteration** | Phase-locked | Fluid, edit anything |
|
||||
| **Editor Support** | 18+ configurator classes | Single skills directory |
|
||||
| **Editor Support** | Tool-specific configurator/adapters | Single skills directory |
|
||||
|
||||
## Schemas
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Core Concepts at a Glance
|
||||
|
||||
**OpenSpec is a lightweight agreement layer between you and your AI.** You write down what a change should do, the AI drafts the details, you both look at the same plan, and only then does code get written. This page is the whole mental model on one screen. When you want the long version, [Concepts](concepts.md) has it.
|
||||
|
||||
Here's the entire idea in five words: **agree first, then build confidently.**
|
||||
|
||||
## The five ideas
|
||||
|
||||
Everything in OpenSpec is built from five concepts. Learn these and the rest is detail.
|
||||
|
||||
**1. Specs are the truth.** A spec describes how your system behaves *right now*. It lives in `openspec/specs/`, organized by domain (`auth/`, `payments/`, `ui/`). Specs are made of requirements ("the system SHALL expire sessions after 30 minutes") and scenarios (concrete given/when/then examples). Think of specs as the single agreed-upon answer to "what does this software do?"
|
||||
|
||||
**2. A change is one unit of work.** When you want to add, modify, or remove behavior, you create a change: a folder in `openspec/changes/` holding everything about that work in one place. A proposal, a design, a task list, and the spec edits. One change, one folder, one feature.
|
||||
|
||||
**3. Delta specs describe what's changing, not the whole world.** Inside a change, you don't rewrite the entire spec. You write a small delta: `ADDED` this requirement, `MODIFIED` that one, `REMOVED` this other one. This is the trick that makes OpenSpec good at editing existing systems, not just green-field ones. You describe the diff, not the destination.
|
||||
|
||||
**4. Artifacts build on each other.** A change contains a few documents, created in a natural order, each feeding the next:
|
||||
|
||||
```text
|
||||
proposal ──► specs ──► design ──► tasks ──► implement
|
||||
why what how steps do it
|
||||
```
|
||||
|
||||
You can revisit any of them at any time. They're enablers, not gates. (More on that below.)
|
||||
|
||||
**5. Archiving folds the change back into the truth.** When the work is done, you archive the change. Its delta specs merge into your main specs, and the change folder moves to `changes/archive/` with a date stamp. Now your specs describe the new reality, and you're ready for the next change. The cycle closes.
|
||||
|
||||
## The picture
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ openspec/ │
|
||||
│ │
|
||||
│ ┌──────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ specs/ │ │ changes/ │ │
|
||||
│ │ │ ◄───── │ │ │
|
||||
│ │ source of truth │ merge │ one folder per change │ │
|
||||
│ │ how things work │ on │ proposal · design · │ │
|
||||
│ │ today │ archive │ tasks · delta specs │ │
|
||||
│ └──────────────────┘ └──────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Two folders. `specs/` is what's true. `changes/` is what you're proposing. Archiving moves a proposal into truth.
|
||||
|
||||
## The loop you'll actually run
|
||||
|
||||
In the default setup, your day looks like this. Optionally think it through first; then one command drafts the plan, you read it, the next builds it, and the last files it away.
|
||||
|
||||
```text
|
||||
/opsx:explore → (optional) think it through with the AI first
|
||||
/opsx:propose add-dark-mode → AI drafts proposal, specs, design, tasks
|
||||
(you read and adjust the plan)
|
||||
/opsx:apply → AI builds it, checking off tasks
|
||||
/opsx:archive → specs updated, change archived
|
||||
```
|
||||
|
||||
**When in doubt, start by exploring.** `/opsx:explore` is a no-stakes thinking partner: it reads your code, lays out options, and turns a fuzzy idea into a concrete plan before any artifact exists. It's the best antidote to an AI that will otherwise build *something* from a vague prompt. Already know exactly what you want? Skip straight to `/opsx:propose`. Either way, explore ships in the default profile, so it's always there. See the [Explore guide](explore.md).
|
||||
|
||||
Those are slash commands, typed in your AI assistant's chat. Setup (`openspec init`) happens in your terminal. If that split is new to you, read [How Commands Work](how-commands-work.md) first; it's the most common point of confusion.
|
||||
|
||||
## "Enablers, not gates"
|
||||
|
||||
This phrase shows up everywhere in OpenSpec, so here's what it means in plain terms.
|
||||
|
||||
Old-school spec processes are waterfalls: finish planning, *then* you're allowed to implement, and going back is painful. OpenSpec refuses that. The order `proposal → specs → design → tasks` shows what becomes *possible* next, not what you're *forced* to do next.
|
||||
|
||||
Discover during implementation that the design was wrong? Edit `design.md` and keep going. Realize the scope should shrink? Update the proposal. Nothing locks. The dependencies exist only so the AI has the context it needs (you can't write good tasks without specs to base them on), not to box you in.
|
||||
|
||||
The strength here is honesty: real work is messy and iterative, and OpenSpec lets it be. The tradeoff is discipline: because nothing forces you forward, it's on you to keep a change focused rather than letting it sprawl. The [Workflows](workflows.md) guide has good habits for that.
|
||||
|
||||
## Why this is worth the small overhead
|
||||
|
||||
Plain truth: OpenSpec adds a step. You write a short plan before building. So what do you get for it?
|
||||
|
||||
- **You catch wrong turns before they cost you.** Fixing a misunderstanding in a one-paragraph proposal is free. Fixing it after the AI wrote 400 lines is not.
|
||||
- **The plan and the code stay in the same repo.** Six months later, the spec tells you (and the next AI session) why the system works the way it does.
|
||||
- **Changes are reviewable.** A change folder is a tidy package: read the proposal, skim the deltas, check the tasks. No archaeology through chat history.
|
||||
- **It fits existing codebases.** Deltas mean you can specify a change to a 50,000-line app without first documenting the whole thing.
|
||||
|
||||
And the honest tradeoff: for a truly trivial one-line fix, the ceremony may not pay off, and that's fine. OpenSpec is designed to be lightweight, but it isn't free. Use it where agreement matters, which turns out to be most of the time once you're working with an AI that will confidently build whatever you vaguely asked for.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- New here? [Getting Started](getting-started.md) walks the first change in full.
|
||||
- Not sure what to build yet? [Explore First](explore.md) is the place to start.
|
||||
- Confused about where commands run? [How Commands Work](how-commands-work.md).
|
||||
- Want the deep version of everything above? [Concepts](concepts.md).
|
||||
- Learn by example? [Examples & Recipes](examples.md).
|
||||
- Need a term defined? [Glossary](glossary.md).
|
||||
@@ -0,0 +1,143 @@
|
||||
# Reviewing a Change
|
||||
|
||||
OpenSpec's whole promise is that you and your AI **agree on what to build before any code is written.** That agreement only means something if you actually read what the AI drafted. This page is about the two minutes where you do that — what to open, in what order, and what to look for.
|
||||
|
||||
The bet is simple: catching a wrong turn in a one-paragraph plan is nearly free. Catching the same wrong turn in 300 lines of code is not. Review is where you collect on that bet.
|
||||
|
||||
## The two moments you review
|
||||
|
||||
There are exactly two:
|
||||
|
||||
```
|
||||
/opsx:propose ──► REVIEW THE PLAN ──► /opsx:apply ──► REVIEW THE CODE ──► /opsx:archive
|
||||
(before any code) (/opsx:verify)
|
||||
```
|
||||
|
||||
1. **After `/opsx:propose`** (or `/opsx:ff`), before `/opsx:apply` — read the plan while it's still just words.
|
||||
2. **After building**, with `/opsx:verify` — check that the code actually did what the plan said.
|
||||
|
||||
The first review is the one that saves you the most, and the one people skip. This page spends most of its time there.
|
||||
|
||||
## Read it in this order
|
||||
|
||||
A change is a folder of plain Markdown in `openspec/changes/<name>/`. Read the files in the order that lets you quit earliest if something's wrong:
|
||||
|
||||
```
|
||||
openspec/changes/add-dark-mode/
|
||||
├── proposal.md 1. the intent and scope ← if this is wrong, stop here
|
||||
├── specs/…/spec.md 2. the requirements ← the heart of the review
|
||||
├── design.md (only for bigger changes) — the technical approach
|
||||
└── tasks.md 3. the plan of work
|
||||
```
|
||||
|
||||
You don't need to read every line. You need to answer three questions, one per file.
|
||||
|
||||
## The proposal: is this the right problem?
|
||||
|
||||
Open `proposal.md` first. It captures the "why" and "what" — the intent, the scope, the approach in a paragraph or two.
|
||||
|
||||
**What good looks like:** one clear intent, a scope you recognize, and a reason this is worth doing now.
|
||||
|
||||
**Red flags:**
|
||||
|
||||
- It solves a slightly *different* problem than the one you asked for.
|
||||
- The scope has grown — you asked for a theme toggle and the proposal also touches auth "while we're in there."
|
||||
- It's vague. "Improve the settings page" is not a scope; "add a dark-mode toggle that respects the OS preference" is.
|
||||
|
||||
**The question to answer:** *Does this match what I actually asked for, and is anything sneaking in?* If the answer is no, stop — don't read further, fix the proposal (see [Pushing back](#pushing-back-is-cheap)).
|
||||
|
||||
## The spec deltas: is "done" defined correctly?
|
||||
|
||||
This is the heart of the review. The delta specs under `specs/` say what will be *true* when the change ships — as requirements and the scenarios that prove them:
|
||||
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dark Mode Toggle
|
||||
The system SHALL let a user switch between light and dark themes.
|
||||
|
||||
#### Scenario: Respects the OS preference on first load
|
||||
- GIVEN a user who has never set a theme
|
||||
- WHEN they open the app on a device set to dark mode
|
||||
- THEN the app renders in dark mode
|
||||
```
|
||||
|
||||
**What a good requirement looks like:** one clear `SHALL`/`MUST` statement you could hand to a tester, and at least one scenario whose GIVEN/WHEN/THEN actually exercises that statement.
|
||||
|
||||
**Red flags:**
|
||||
|
||||
- **A vague requirement.** "The system SHALL be fast" can't be built or tested. What's fast?
|
||||
- **A requirement with no scenario**, or a scenario that doesn't test the requirement it sits under.
|
||||
- **The most valuable catch of all: what's missing.** The AI faithfully writes down what you *said*. Your job is to notice what you *forgot* to say. If you cared most about the OS-preference case and no scenario mentions it, that's the review paying for itself.
|
||||
|
||||
Read the deltas asking *would I be happy if the system did exactly — and only — this?* Nothing here is about code yet, so it stays cheap to change.
|
||||
|
||||
## The tasks: is the plan of work sane?
|
||||
|
||||
Open `tasks.md` last. It's the implementation checklist the AI will work through.
|
||||
|
||||
**What good looks like:** ordered steps, each traceable to a requirement, nothing mysterious.
|
||||
|
||||
**Red flags:**
|
||||
|
||||
- A task with no matching requirement (where did that come from?).
|
||||
- One giant "implement the feature" task that hides all the real decisions.
|
||||
- A task that touches something outside the scope you just approved.
|
||||
|
||||
You're not estimating or micromanaging here — you're checking that the plan matches the requirements you already accepted.
|
||||
|
||||
## Pushing back is cheap
|
||||
|
||||
If any of the three questions came back wrong, say so. There are no phases and nothing is locked — you fix it and move on. Two ways, exactly as in [Editing a change](editing-changes.md):
|
||||
|
||||
- **Edit the file yourself.** It's plain Markdown; change the scope line, tighten a requirement, delete a task.
|
||||
- **Tell the AI what's wrong** and let it revise: *"drop the auth changes — out of scope,"* *"add a scenario for when the user has already picked a theme,"* *"split task 3 into schema and UI."*
|
||||
|
||||
Then re-read the part you changed. Re-draft until it's a plan you'd sign your name to. That back-and-forth *is* the product working.
|
||||
|
||||
## After the code: verify
|
||||
|
||||
Once the work is built, `/opsx:verify` is your second review. It re-reads the artifacts and the code and reports mismatches across three dimensions:
|
||||
|
||||
| Dimension | What it checks |
|
||||
|-----------|----------------|
|
||||
| **Completeness** | Every task done, every requirement implemented, scenarios covered |
|
||||
| **Correctness** | The implementation matches the spec's intent, edge cases handled |
|
||||
| **Coherence** | Design decisions actually show up in the code |
|
||||
|
||||
```
|
||||
You: /opsx:verify
|
||||
|
||||
AI: Verifying add-dark-mode...
|
||||
|
||||
COMPLETENESS
|
||||
✓ All 8 tasks in tasks.md are checked
|
||||
✓ All requirements in specs have corresponding code
|
||||
⚠ Scenario "Respects the OS preference on first load" has no test coverage
|
||||
```
|
||||
|
||||
It flags issues as CRITICAL, WARNING, or SUGGESTION, and it does **not** block archiving — it surfaces the gaps and leaves the call to you. This is the difference between "did the AI write code" and "did it build what we agreed."
|
||||
|
||||
`/opsx:verify` is in the expanded profile. If you don't have it, turn it on with `openspec config profile` (then `openspec update`), or just re-read the change and the diff yourself.
|
||||
|
||||
## Right-size the review
|
||||
|
||||
Not every change earns the full pass. A one-file typo fix deserves a twenty-second skim. A change that touches auth, payments, or data you can't recover deserves every question above. The point was never ceremony — it's spending your attention where a mistake would be expensive, and skimming where it wouldn't.
|
||||
|
||||
## The two-minute checklist
|
||||
|
||||
- [ ] The proposal's intent matches what I asked for.
|
||||
- [ ] Nothing extra has crept into the scope.
|
||||
- [ ] Every requirement is specific enough to test.
|
||||
- [ ] Every requirement has a scenario that actually exercises it.
|
||||
- [ ] The case I care about most is covered.
|
||||
- [ ] Tasks map to requirements; nothing is mysterious or out of scope.
|
||||
- [ ] I'd be comfortable if the AI built exactly this and nothing more.
|
||||
|
||||
If all seven pass, run `/opsx:apply` with confidence. If any fail, that's not a setback — it's the two minutes doing its job.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Writing Good Specs](writing-specs.md) — the flip side: how to draft requirements and scenarios worth approving.
|
||||
- [Editing & Iterating on a Change](editing-changes.md) — the mechanics of changing a plan after you've started.
|
||||
- [Workflows](workflows.md) — where review fits in the larger loop.
|
||||
@@ -0,0 +1,349 @@
|
||||
# Stores: Plan in Its Own Repo
|
||||
|
||||
> **Beta.** Stores, references, working context, and worksets are
|
||||
> new. Command names, flags, file formats, and JSON output may still change
|
||||
> shape between releases. Every walkthrough below was run against the
|
||||
> current build, but re-read this guide after upgrading.
|
||||
|
||||
## The problem this solves
|
||||
|
||||
OpenSpec normally lives inside one code repo: an `openspec/` folder next to
|
||||
your code, holding specs and changes for that repo.
|
||||
|
||||
That stops fitting the moment your planning is bigger than one repo:
|
||||
|
||||
- Your work spans several repos — one feature touches the API server, the
|
||||
web app, and a shared library. Whose `openspec/` folder does the plan
|
||||
live in?
|
||||
- Your team plans before code exists, or plans things that never become
|
||||
code in *this* repo.
|
||||
- Requirements are owned by one team and consumed by others. The wiki
|
||||
version drifts, and your coding agent can't read it anyway.
|
||||
|
||||
A **store** is the answer: a standalone repo whose whole job is planning.
|
||||
It has the same `openspec/` shape you already know — specs and changes —
|
||||
plus a small identity file. You register it on your machine once, by name,
|
||||
and then every normal OpenSpec command can work in it from anywhere.
|
||||
|
||||
## The shape
|
||||
|
||||
```
|
||||
team-plans (a store: planning in its own repo)
|
||||
├── .openspec-store/store.yaml identity: "I am team-plans"
|
||||
└── openspec/
|
||||
├── specs/ what is true
|
||||
└── changes/ what is in motion
|
||||
▲
|
||||
│ registered on each machine by name;
|
||||
│ shared by pushing/cloning like any repo
|
||||
┌─────────────┼─────────────┐
|
||||
│ │ │
|
||||
web-app api-server mobile-app
|
||||
(code repo) (code repo) (code repo)
|
||||
```
|
||||
|
||||
Two rules keep this simple:
|
||||
|
||||
1. **A store is just a git repo.** You commit, push, pull, and review it
|
||||
yourself. OpenSpec never clones, syncs, or pushes anything on its own.
|
||||
2. **Declarations, not machinery.** Repos can *declare* how they relate to
|
||||
stores (shown below). Declarations change what OpenSpec can tell you —
|
||||
never where your commands act.
|
||||
|
||||
## Five minutes to your first store
|
||||
|
||||
Two commands take you from nothing to a working, store-scoped change:
|
||||
|
||||
```bash
|
||||
openspec store setup team-plans --path ~/openspec/team-plans
|
||||
```
|
||||
|
||||
```
|
||||
Store ready: team-plans
|
||||
Location: /Users/you/openspec/team-plans
|
||||
OpenSpec root: ready
|
||||
Registry: registered
|
||||
|
||||
Next: run normal OpenSpec commands against this store, for example:
|
||||
openspec new change <change-id> --store team-plans
|
||||
Share this store by committing and pushing it like any Git repo.
|
||||
```
|
||||
|
||||
```bash
|
||||
openspec new change add-login --store team-plans
|
||||
```
|
||||
|
||||
```
|
||||
Using OpenSpec root: team-plans (/Users/you/openspec/team-plans)
|
||||
Created change 'add-login' at /Users/you/openspec/team-plans/openspec/changes/add-login/
|
||||
Schema: spec-driven
|
||||
Next: openspec status --change add-login --store team-plans
|
||||
```
|
||||
|
||||
That's the whole model. From here the lifecycle is exactly what you know —
|
||||
`status`, `instructions`, `validate`, `archive` — with `--store team-plans`
|
||||
on each command, and every printed hint carries the flag for you. The
|
||||
`Using OpenSpec root:` line always tells you where a command is acting.
|
||||
|
||||
## Story: one team, one planning repo
|
||||
|
||||
A team keeps its specs and changes in `team-plans` instead of scattering
|
||||
them across code repos.
|
||||
|
||||
**Day one (whoever sets it up):**
|
||||
|
||||
```bash
|
||||
openspec store setup team-plans --path ~/openspec/team-plans \
|
||||
--remote git@github.com:acme/team-plans.git
|
||||
git -C ~/openspec/team-plans push -u origin main
|
||||
```
|
||||
|
||||
Passing `--remote` records the clone URL inside the store's own identity
|
||||
file (`.openspec-store/store.yaml`), in the initial commit. Every future
|
||||
clone is born knowing where it came from, so health checks and error
|
||||
messages can print a complete, pasteable fix for teammates who don't have
|
||||
it yet.
|
||||
|
||||
**Every teammate (once per machine):**
|
||||
|
||||
```bash
|
||||
git clone git@github.com:acme/team-plans.git ~/openspec/team-plans
|
||||
openspec store register ~/openspec/team-plans
|
||||
```
|
||||
|
||||
From then on, everyone works in the same planning repo by name:
|
||||
|
||||
```bash
|
||||
openspec status --store team-plans --change add-login
|
||||
openspec show add-login --store team-plans
|
||||
```
|
||||
|
||||
**Sharing work is git, on purpose.** A change you create exists only in
|
||||
your checkout until you commit and push it — same as code. Plans get
|
||||
branches, pull requests, and review for free, because a store is an
|
||||
ordinary repo.
|
||||
|
||||
**Connecting the team's code repos.** A code repo whose planning is fully
|
||||
externalized needs exactly one line, in `openspec/config.yaml`:
|
||||
|
||||
```yaml
|
||||
# web-app/openspec/config.yaml
|
||||
store: team-plans
|
||||
```
|
||||
|
||||
Now every OpenSpec command run inside `web-app` acts on `team-plans` with
|
||||
no flags at all:
|
||||
|
||||
```bash
|
||||
cd ~/src/web-app
|
||||
openspec status --change add-login
|
||||
```
|
||||
|
||||
```
|
||||
Using OpenSpec root: team-plans (/Users/you/openspec/team-plans)
|
||||
...
|
||||
```
|
||||
|
||||
The pointer is a fallback, never an override: an explicit `--store` always
|
||||
wins, and if the repo grows real planning folders of its own, those win
|
||||
(with a warning to remove the stale pointer).
|
||||
|
||||
## Story: requirements that cross team lines
|
||||
|
||||
A platform team owns the requirements. Product teams build against them,
|
||||
in their own repos, with their own designs. A reference describes that
|
||||
relationship without moving anyone's work.
|
||||
|
||||
```
|
||||
platform-reqs (store) api-server (code repo)
|
||||
owned by the platform team owned by a product team
|
||||
┌──────────────────────────┐ ┌──────────────────────────┐
|
||||
│ openspec/specs/ │ ◀────────│ openspec/config.yaml │
|
||||
│ payments/spec.md │ reads │ references: │
|
||||
│ auth/spec.md │ │ - platform-reqs │
|
||||
│ │ │ openspec/specs/ │
|
||||
│ openspec/changes/ │ │ (their own designs) │
|
||||
│ platform work │ │ openspec/changes/ │
|
||||
│ │ │ (their own work) │
|
||||
│ │ └──────────────────────────┘
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
**The product team declares what it draws on** in its repo's
|
||||
`openspec/config.yaml`:
|
||||
|
||||
```yaml
|
||||
references:
|
||||
- platform-reqs
|
||||
```
|
||||
|
||||
References are read-only context. The repo keeps its own `openspec/` root;
|
||||
work stays there. What changes: `openspec instructions` in that repo now
|
||||
includes an index of the referenced store's specs — each with a one-line
|
||||
summary and the exact fetch command (`openspec show <spec-id> --type spec
|
||||
--store platform-reqs`). An agent working in `api-server` can find the
|
||||
upstream payment requirements, cite them, and write its low-level design in
|
||||
the repo's own root — without anyone pasting context around.
|
||||
|
||||
A reference can carry its clone source, so teammates who don't have the
|
||||
store yet get a complete fix instead of a dead end:
|
||||
|
||||
```yaml
|
||||
references:
|
||||
- { id: platform-reqs, remote: "git@github.com:acme/platform-reqs.git" }
|
||||
```
|
||||
|
||||
**When you want the plan and code open together, make a workset.** This is
|
||||
personal and explicit: each person chooses the folders they actually work
|
||||
with on their machine. Nothing about those local checkout paths is
|
||||
committed to the shared planning repo.
|
||||
|
||||
```bash
|
||||
openspec workset create platform \
|
||||
--member ~/openspec/platform-reqs \
|
||||
--member ~/src/api-server \
|
||||
--member ~/src/web-app
|
||||
```
|
||||
|
||||
## Two questions you can always ask
|
||||
|
||||
**"Is my setup healthy?"** — `openspec doctor` checks the current root and
|
||||
its referenced stores, read-only, with a pasteable fix per finding:
|
||||
|
||||
```
|
||||
Doctor
|
||||
|
||||
Root
|
||||
Location: /Users/you/src/api-server
|
||||
OpenSpec root: ok
|
||||
|
||||
References
|
||||
- platform-reqs: ok (/Users/you/openspec/platform-reqs)
|
||||
- design-system: Referenced store 'design-system' is not registered on this machine.
|
||||
Fix: git clone -- git@github.com:acme/design-system.git '/Users/you/openspec/design-system' && openspec store register '/Users/you/openspec/design-system' --id design-system
|
||||
|
||||
```
|
||||
|
||||
**"What am I working with?"** — `openspec context` assembles the working
|
||||
set from OpenSpec declarations: the root and the stores it references.
|
||||
|
||||
```
|
||||
Working context for api-server (/Users/you/src/api-server)
|
||||
|
||||
OpenSpec root
|
||||
api-server /Users/you/src/api-server
|
||||
|
||||
Referenced stores
|
||||
platform-reqs /Users/you/openspec/platform-reqs
|
||||
Fetch: openspec show <spec-id> --type spec --store platform-reqs
|
||||
```
|
||||
|
||||
Both support `--json` for agents. `openspec context --code-workspace
|
||||
<path>` additionally writes a VS Code workspace file containing the whole
|
||||
set — the only write this command performs.
|
||||
|
||||
## Worksets: reopen the folders you work on together
|
||||
|
||||
Separate from all of the above: most people open the same few folders
|
||||
together every session — the planning repo plus two or three code repos.
|
||||
A **workset** is a personal, named view of exactly that, reopened with one
|
||||
command in your tool of choice.
|
||||
|
||||
```
|
||||
workset "platform" openspec workset open platform
|
||||
├── team-plans ~/openspec/team-plans │
|
||||
├── api-server ~/src/api-server ▼
|
||||
└── web-app ~/src/web-app all three open in your tool
|
||||
```
|
||||
|
||||
```bash
|
||||
openspec workset create platform \
|
||||
--member ~/openspec/team-plans --member ~/src/api-server \
|
||||
--tool code
|
||||
openspec workset list
|
||||
```
|
||||
|
||||
```
|
||||
platform (opens in VS Code)
|
||||
team-plans /Users/you/openspec/team-plans
|
||||
api-server /Users/you/src/api-server
|
||||
```
|
||||
|
||||
`openspec workset open platform` then launches the saved tool: editors
|
||||
(VS Code, Cursor) open one window with every member and return. The first
|
||||
member is the primary. Override the tool any time with `--tool <id>`.
|
||||
|
||||
Worksets are deliberately *not* shared state. They live on your machine,
|
||||
are never committed, and make no claims about the work — they only record
|
||||
what you like open together. Removing one never touches the member
|
||||
folders. New tools are configuration, not code: anything launched via a
|
||||
workspace file or per-folder attach flags can be added under the `openers`
|
||||
key in the global config (`openspec config edit`).
|
||||
|
||||
## How commands decide where to act
|
||||
|
||||
Every normal command resolves its root the same way, in this order:
|
||||
|
||||
```
|
||||
1. --store <id> you said so explicitly → that store
|
||||
2. nearest openspec/ a real planning root here → this repo
|
||||
(walking up from cwd)
|
||||
3. store: pointer config.yaml declares a store → that store
|
||||
4. none of the above stores registered on this → error with a
|
||||
machine? selection hint
|
||||
no stores registered? → the current
|
||||
directory
|
||||
(classic behavior)
|
||||
```
|
||||
|
||||
The `Using OpenSpec root:` line (and the `root` block in `--json` output)
|
||||
tells you which case you're in.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- **Beta shape.** Everything on this page may change between releases —
|
||||
names, flags, file formats, JSON keys.
|
||||
- **One checkout per store id per machine.** Registering a second checkout
|
||||
under the same id fails with a hint to `store unregister` first.
|
||||
- **No sync, ever — by design.** OpenSpec never clones, pulls, or pushes.
|
||||
A stale checkout shows stale specs until *you* pull; references are
|
||||
indexed live from whatever is on disk.
|
||||
- **Empty planning folders can be absent.** A new store may not have
|
||||
`openspec/changes/`, `openspec/specs/`, or `openspec/changes/archive/` in Git
|
||||
yet. That is accepted during the beta; those folders appear once normal
|
||||
commands create files for them.
|
||||
- **Pointer repos stay pointers.** A config-only repo whose
|
||||
`openspec/config.yaml` declares `store: <id>` is treated as externalized
|
||||
planning, not as a store checkout to register. Remove the `store:` line first
|
||||
if you intentionally want to convert that repo into a local store root.
|
||||
- **Some commands stay where they are.** `view`, `templates`, `schemas`,
|
||||
and the deprecated noun forms (`openspec change show`, ...) act on the
|
||||
current directory only — no `--store`.
|
||||
- **Per-machine state is per-machine.** The store registry and worksets
|
||||
are local settings. Nothing about your machine's layout is
|
||||
ever committed to shared planning.
|
||||
- **Two launch styles for worksets.** A tool that can't be launched with a
|
||||
workspace file or per-folder attach flags can't be added as an opener.
|
||||
- **Agent JSON has a known casing split** (store-family keys are
|
||||
snake_case, workflow-family camelCase). Documented in the
|
||||
[agent contract](../agent-contract.md); unifying it is deferred to a
|
||||
versioned release.
|
||||
|
||||
## Where things live
|
||||
|
||||
| What | Where | Shared? |
|
||||
|---|---|---|
|
||||
| A store's planning | `<store>/openspec/` (specs, changes) | Yes — commit and push it |
|
||||
| A store's identity | `<store>/.openspec-store/store.yaml` | Yes — committed with the store |
|
||||
| The store registry | `<data dir>/openspec/stores/registry.yaml` | No — this machine only |
|
||||
| Worksets | `<data dir>/openspec/worksets/` | No — this machine only |
|
||||
|
||||
`<data dir>` is `~/.local/share/openspec` on macOS and Linux (or
|
||||
`$XDG_DATA_HOME/openspec` when set), and `%LOCALAPPDATA%\openspec` on
|
||||
Windows.
|
||||
|
||||
## Reference
|
||||
|
||||
Exact flags and JSON shapes for every command on this page:
|
||||
[CLI reference](../cli.md) (Stores, Doctor, Working context, Personal
|
||||
worksets) and the [agent contract](../agent-contract.md).
|
||||
+75
-52
@@ -1,50 +1,66 @@
|
||||
# Supported Tools
|
||||
|
||||
OpenSpec works with 20+ AI coding assistants. When you run `openspec init`, you'll be prompted to select which tools you use, and OpenSpec will configure the appropriate integrations.
|
||||
OpenSpec works with many AI coding assistants. When you run `openspec init`, OpenSpec configures selected tools using your active profile/workflow selection and delivery mode.
|
||||
|
||||
## How It Works
|
||||
|
||||
For each tool you select, OpenSpec installs:
|
||||
For each selected tool, OpenSpec can install:
|
||||
|
||||
1. **Skills** — Reusable instruction files that power the `/opsx:*` workflow commands
|
||||
2. **Commands** — Tool-specific slash command bindings
|
||||
1. **Skills** (if delivery includes skills): `.../skills/openspec-*/SKILL.md`
|
||||
2. **Commands** (if delivery includes commands): tool-specific `opsx-*` command files
|
||||
|
||||
By default, OpenSpec uses the `core` profile, which includes:
|
||||
- `propose`
|
||||
- `explore`
|
||||
- `apply`
|
||||
- `sync`
|
||||
- `archive`
|
||||
|
||||
You can enable expanded workflows (`new`, `continue`, `ff`, `verify`, `bulk-archive`, `onboard`) via `openspec config profile`, then run `openspec update`.
|
||||
|
||||
## Tool Directory Reference
|
||||
|
||||
| Tool | Skills Location | Commands Location |
|
||||
|------|-----------------|-------------------|
|
||||
| Amazon Q Developer | `.amazonq/skills/` | `.amazonq/prompts/` |
|
||||
| Antigravity | `.agent/skills/` | `.agent/workflows/` |
|
||||
| Auggie (Augment CLI) | `.augment/skills/` | `.augment/commands/` |
|
||||
| Claude Code | `.claude/skills/` | `.claude/commands/opsx/` |
|
||||
| Cline | `.cline/skills/` | `.clinerules/workflows/` |
|
||||
| CodeBuddy | `.codebuddy/skills/` | `.codebuddy/commands/opsx/` |
|
||||
| Codex | `.codex/skills/` | `~/.codex/prompts/`\* |
|
||||
| Continue | `.continue/skills/` | `.continue/prompts/` |
|
||||
| CoStrict | `.cospec/skills/` | `.cospec/openspec/commands/` |
|
||||
| Crush | `.crush/skills/` | `.crush/commands/opsx/` |
|
||||
| Cursor | `.cursor/skills/` | `.cursor/commands/` |
|
||||
| Factory Droid | `.factory/skills/` | `.factory/commands/` |
|
||||
| Gemini CLI | `.gemini/skills/` | `.gemini/commands/opsx/` |
|
||||
| GitHub Copilot | `.github/skills/` | `.github/prompts/`\*\* |
|
||||
| iFlow | `.iflow/skills/` | `.iflow/commands/` |
|
||||
| Kilo Code | `.kilocode/skills/` | `.kilocode/workflows/` |
|
||||
| Kiro | `.kiro/skills/` | `.kiro/prompts/` |
|
||||
| OpenCode | `.opencode/skills/` | `.opencode/command/` |
|
||||
| Pi | `.pi/skills/` | `.pi/prompts/` |
|
||||
| Qoder | `.qoder/skills/` | `.qoder/commands/opsx/` |
|
||||
| Qwen Code | `.qwen/skills/` | `.qwen/commands/` |
|
||||
| RooCode | `.roo/skills/` | `.roo/commands/` |
|
||||
| Trae | `.trae/skills/` | `.trae/skills/` (via `/openspec-*`) |
|
||||
| Windsurf | `.windsurf/skills/` | `.windsurf/workflows/` |
|
||||
| Tool (ID) | Skills path pattern | Command path pattern |
|
||||
|-----------|---------------------|----------------------|
|
||||
| Amazon Q Developer (`amazon-q`) | `.amazonq/skills/openspec-*/SKILL.md` | `.amazonq/prompts/opsx-<id>.md` |
|
||||
| Antigravity (`antigravity`) | `.agent/skills/openspec-*/SKILL.md` | `.agent/workflows/opsx-<id>.md` |
|
||||
| Auggie (`auggie`) | `.augment/skills/openspec-*/SKILL.md` | `.augment/commands/opsx-<id>.md` |
|
||||
| IBM Bob Shell (`bob`) | `.bob/skills/openspec-*/SKILL.md` | `.bob/commands/opsx-<id>.md` |
|
||||
| Claude Code (`claude`) | `.claude/skills/openspec-*/SKILL.md` | `.claude/commands/opsx/<id>.md` |
|
||||
| Cline (`cline`) | `.cline/skills/openspec-*/SKILL.md` | `.clinerules/workflows/opsx-<id>.md` |
|
||||
| CodeBuddy (`codebuddy`) | `.codebuddy/skills/openspec-*/SKILL.md` | `.codebuddy/commands/opsx/<id>.md` |
|
||||
| Codex (`codex`) | `.codex/skills/openspec-*/SKILL.md` | `$CODEX_HOME/prompts/opsx-<id>.md`\* |
|
||||
| ForgeCode (`forgecode`) | `.forge/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/openspec-*` invocations) |
|
||||
| Continue (`continue`) | `.continue/skills/openspec-*/SKILL.md` | `.continue/prompts/opsx-<id>.prompt` |
|
||||
| CoStrict (`costrict`) | `.cospec/skills/openspec-*/SKILL.md` | `.cospec/openspec/commands/opsx-<id>.md` |
|
||||
| Crush (`crush`) | `.crush/skills/openspec-*/SKILL.md` | `.crush/commands/opsx/<id>.md` |
|
||||
| Cursor (`cursor`) | `.cursor/skills/openspec-*/SKILL.md` | `.cursor/commands/opsx-<id>.md` |
|
||||
| Factory Droid (`factory`) | `.factory/skills/openspec-*/SKILL.md` | `.factory/commands/opsx-<id>.md` |
|
||||
| Gemini CLI (`gemini`) | `.gemini/skills/openspec-*/SKILL.md` | `.gemini/commands/opsx/<id>.toml` |
|
||||
| GitHub Copilot (`github-copilot`) | `.github/skills/openspec-*/SKILL.md` | `.github/prompts/opsx-<id>.prompt.md`\*\* |
|
||||
| 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` |
|
||||
| Kimi CLI (`kimi`) | `.kimi/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` |
|
||||
| Mistral Vibe (`vibe`) | `.vibe/skills/openspec-*/SKILL.md` | Not generated (no command adapter; use skill-based `/openspec-*` invocations) |
|
||||
| Oh My Pi (`oh-my-pi`) | `.omp/skills/openspec-*/SKILL.md` | `.omp/commands/opsx-<id>.md` |
|
||||
| OpenCode (`opencode`) | `.opencode/skills/openspec-*/SKILL.md` | `.opencode/commands/opsx-<id>.md` |
|
||||
| Pi (`pi`) | `.pi/skills/openspec-*/SKILL.md` | `.pi/prompts/opsx-<id>.md` |
|
||||
| Qoder (`qoder`) | `.qoder/skills/openspec-*/SKILL.md` | `.qoder/commands/opsx/<id>.md` |
|
||||
| Qwen Code (`qwen`) | `.qwen/skills/openspec-*/SKILL.md` | `.qwen/commands/opsx-<id>.toml` |
|
||||
| RooCode (`roocode`) | `.roo/skills/openspec-*/SKILL.md` | `.roo/commands/opsx-<id>.md` |
|
||||
| Trae (`trae`) | `.trae/skills/openspec-*/SKILL.md` | `.trae/commands/opsx-<id>.md` |
|
||||
| Windsurf (`windsurf`) | `.windsurf/skills/openspec-*/SKILL.md` | `.windsurf/workflows/opsx-<id>.md` |
|
||||
|
||||
\* Codex commands are installed to the global home directory (`~/.codex/prompts/` or `$CODEX_HOME/prompts/`), not the project directory.
|
||||
\* Codex commands are installed in the global Codex home (`$CODEX_HOME/prompts/` if set, otherwise `~/.codex/prompts/`), not your project directory.
|
||||
|
||||
\*\* GitHub Copilot's `.github/prompts/*.prompt.md` files are recognized as custom slash commands in **IDE extensions only** (VS Code, JetBrains, Visual Studio). GitHub Copilot CLI does not currently support custom prompts from this directory — see [github/copilot-cli#618](https://github.com/github/copilot-cli/issues/618). If you use Copilot CLI, you may need to manually set up [custom agents](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/create-custom-agents) in `.github/agents/` as a workaround.
|
||||
\*\* GitHub Copilot prompt files are recognized as custom slash commands in IDE extensions (VS Code, JetBrains, Visual Studio). Copilot CLI does not currently consume `.github/prompts/*.prompt.md` directly.
|
||||
|
||||
## Non-Interactive Setup
|
||||
|
||||
For CI/CD or scripted setup, use the `--tools` flag:
|
||||
For CI/CD or scripted setup, use `--tools` (and optionally `--profile`):
|
||||
|
||||
```bash
|
||||
# Configure specific tools
|
||||
@@ -55,34 +71,41 @@ openspec init --tools all
|
||||
|
||||
# Skip tool configuration
|
||||
openspec init --tools none
|
||||
|
||||
# Override profile for this init run
|
||||
openspec init --profile core
|
||||
```
|
||||
|
||||
**Available tool IDs:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codebuddy`, `codex`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `kiro`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
|
||||
**Available tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `lingma`, `vibe`, `oh-my-pi`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
|
||||
|
||||
## What Gets Installed
|
||||
## Workflow-Dependent Installation
|
||||
|
||||
For each tool, OpenSpec generates 10 skill files that power the OPSX workflow:
|
||||
OpenSpec installs workflow artifacts based on selected workflows:
|
||||
|
||||
| Skill | Purpose |
|
||||
|-------|---------|
|
||||
| `openspec-explore` | Thinking partner for exploring ideas |
|
||||
| `openspec-new-change` | Start a new change |
|
||||
| `openspec-continue-change` | Create the next artifact |
|
||||
| `openspec-ff-change` | Fast-forward through all planning artifacts |
|
||||
| `openspec-apply-change` | Implement tasks |
|
||||
| `openspec-verify-change` | Verify implementation completeness |
|
||||
| `openspec-sync-specs` | Sync delta specs to main (optional—archive prompts if needed) |
|
||||
| `openspec-archive-change` | Archive a completed change |
|
||||
| `openspec-bulk-archive-change` | Archive multiple changes at once |
|
||||
| `openspec-onboard` | Guided onboarding through a complete workflow cycle |
|
||||
- **Core profile (default):** `propose`, `explore`, `apply`, `sync`, `archive`
|
||||
- **Custom selection:** any subset of all workflow IDs:
|
||||
`propose`, `explore`, `new`, `continue`, `apply`, `ff`, `sync`, `archive`, `bulk-archive`, `verify`, `onboard`
|
||||
|
||||
These skills are invoked via slash commands like `/opsx:new`, `/opsx:apply`, etc. See [Commands](commands.md) for the full list.
|
||||
In other words, skill/command counts are profile-dependent and delivery-dependent, not fixed.
|
||||
|
||||
## Adding a New Tool
|
||||
## Generated Skill Names
|
||||
|
||||
Want to add support for another AI coding assistant? Check out the [command adapter pattern](../CONTRIBUTING.md) or open an issue on GitHub.
|
||||
When selected by profile/workflow config, OpenSpec generates these skills:
|
||||
|
||||
---
|
||||
- `openspec-propose`
|
||||
- `openspec-explore`
|
||||
- `openspec-new-change`
|
||||
- `openspec-continue-change`
|
||||
- `openspec-apply-change`
|
||||
- `openspec-update-change`
|
||||
- `openspec-ff-change`
|
||||
- `openspec-sync-specs`
|
||||
- `openspec-archive-change`
|
||||
- `openspec-bulk-archive-change`
|
||||
- `openspec-verify-change`
|
||||
- `openspec-onboard`
|
||||
|
||||
See [Commands](commands.md) for command behavior and [CLI](cli.md) for `init`/`update` options.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# OpenSpec on a Team
|
||||
|
||||
Everything in the other guides works the same whether you're solo or on a team of twenty. What changes on a team is the questions around the edges: where do the specs live, how do teammates review a plan, and how does any of this fit the pull-request flow we already have?
|
||||
|
||||
The short answer: a change is just files, and OpenSpec never touches git. So it fits your existing workflow instead of replacing it. This page spells out the conventions that work well.
|
||||
|
||||
## One rule: OpenSpec doesn't touch git
|
||||
|
||||
OpenSpec reads and writes plain Markdown under `openspec/`. It never commits, branches, pushes, or pulls in your project — and it never clones or syncs a [store](stores-beta/user-guide.md) on its own. That means:
|
||||
|
||||
- **You commit `openspec/` like any source.** Specs, active changes, and the archive are part of your project's history. (Yes, commit the whole folder — see the [FAQ](faq.md#should-i-commit-the-openspec-folder-to-git).)
|
||||
- **A change is a folder you version like code.** `openspec/changes/add-dark-mode/` is just files on a branch.
|
||||
- **Everything below is convention, not enforcement.** OpenSpec won't make you do it this way; it just fits cleanly.
|
||||
|
||||
## The everyday loop
|
||||
|
||||
The workflow that works well maps a change onto a branch and a pull request:
|
||||
|
||||
```
|
||||
git switch -c add-dark-mode start a branch, as usual
|
||||
│
|
||||
/opsx:propose add-dark-mode draft the plan (proposal + specs + tasks)
|
||||
│
|
||||
REVIEW THE PLAN you read it before any code — see Reviewing a Change
|
||||
│
|
||||
/opsx:apply build it; artifacts + code change together
|
||||
│
|
||||
git commit && open a PR the PR contains the spec delta AND the code
|
||||
│
|
||||
teammate reviews, merges
|
||||
│
|
||||
/opsx:archive fold the delta into specs/, move the change to archive/
|
||||
```
|
||||
|
||||
The plan and the code live side by side in the same branch, so your teammates review both together, and six months later the archived spec still explains why the code looks the way it does.
|
||||
|
||||
## Reviewing specs in a pull request
|
||||
|
||||
This is where a team feels the payoff. When a PR includes the change's delta spec, the reviewer gets something a raw diff never gives them: **a plain-language statement of what this change is supposed to do**, before they read a single line of code.
|
||||
|
||||
A good review order for the reviewer:
|
||||
|
||||
1. **Read `proposal.md`** — is this the right problem and scope?
|
||||
2. **Read the delta under `specs/`** — is "done" defined correctly? (This is the [Reviewing a Change](reviewing-changes.md) two-minute pass, now happening in the PR.)
|
||||
3. **Then read the code diff** — does it deliver exactly those requirements?
|
||||
|
||||
A reviewer who disagrees with the *approach* can say so against the proposal, cheaply, instead of relitigating it across 300 lines of code. Put the delta spec near the top of the PR description, or point reviewers at the change folder, so they start there.
|
||||
|
||||
## When to archive
|
||||
|
||||
Archiving folds a change's deltas into your main `openspec/specs/` and moves the change folder to `openspec/changes/archive/YYYY-MM-DD-<name>/`. Because `specs/` is the **shared source of truth**, the timing matters on a team. Two workable conventions:
|
||||
|
||||
- **Archive after the PR merges (recommended).** The branch carries the active change; once it's merged to your main branch, archive there (often a tiny follow-up commit or a scheduled cleanup). This keeps the shared `specs/` moving forward only with work that actually shipped.
|
||||
- **Archive inside the PR.** Simpler for small teams: the same PR that adds the code also syncs and archives. The tradeoff is that your `specs/` diff and your code diff land together, which can make the PR noisier.
|
||||
|
||||
Pick one and be consistent. Either way, `/opsx:archive` checks that tasks are complete and offers to sync first, so nothing merges half-finished by accident.
|
||||
|
||||
## Two people, parallel changes
|
||||
|
||||
Because changes are separate folders, they don't collide:
|
||||
|
||||
- **Different changes, different people — no problem.** `add-dark-mode` and `rate-limit-login` are different folders on different branches; they never touch each other until they both archive.
|
||||
- **One change, one owner.** Two people editing the same change folder conflict exactly like two people editing the same file. Keep a change to a single author, or split it into two changes (another reason to [right-size](writing-specs.md#right-size-the-change)).
|
||||
- **The one place conflicts show up is `specs/`.** If two changes both modify the *same* requirement, archiving the second one will conflict in `openspec/specs/…/spec.md` — resolve it like any merge conflict, keeping the requirement that reflects reality. This is rare, and it's a feature: it's git telling you two changes disagreed about how the system should behave.
|
||||
|
||||
## When planning outgrows one repo
|
||||
|
||||
Everything above assumes the plan lives in the code repo's own `openspec/` folder, which is the right default. When your planning genuinely spans several repos or teams — one feature touching three services, or requirements one team owns and others consume — that's what the beta **stores** feature is for: planning gets its own repo that any code repo can point at. Start with the [Stores User Guide](stores-beta/user-guide.md).
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Reviewing a Change](reviewing-changes.md) — the review pass, now inside your PR.
|
||||
- [Writing Good Specs](writing-specs.md) — including how to right-size a change so it fits one branch.
|
||||
- [Stores User Guide](stores-beta/user-guide.md) — planning that spans repos and teams.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Troubleshooting
|
||||
|
||||
Concrete fixes for concrete problems. Each entry names a symptom, explains the likely cause in a sentence, and gives you the fix. If you don't see your issue here, the [FAQ](faq.md) may help, and the [Discord](https://discord.gg/YctCnvvshC) definitely will.
|
||||
|
||||
## Installation and setup
|
||||
|
||||
### `openspec: command not found`
|
||||
|
||||
The CLI isn't installed, or your shell can't find it. Install it globally and check:
|
||||
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
openspec --version
|
||||
```
|
||||
|
||||
If it installed but still isn't found, your global npm bin directory probably isn't on your `PATH`. Run `npm bin -g` to see where global binaries live, and make sure that path is in your shell profile.
|
||||
|
||||
### "Requires Node.js 20.19.0 or higher"
|
||||
|
||||
OpenSpec runs on Node 20.19.0+. Check your version and upgrade if needed:
|
||||
|
||||
```bash
|
||||
node --version
|
||||
```
|
||||
|
||||
If you use bun to install OpenSpec, note that OpenSpec still *runs* on Node, so you need Node 20.19.0+ available on your `PATH` regardless. See [Installation](installation.md).
|
||||
|
||||
### `openspec init` didn't configure my AI tool
|
||||
|
||||
Init asks which tools to set up. If you skipped your tool or want to add another, just run it again, or use the non-interactive form:
|
||||
|
||||
```bash
|
||||
openspec init --tools claude,cursor
|
||||
```
|
||||
|
||||
The full list of tool IDs is in [Supported Tools](supported-tools.md). Use `--tools all` for everything, `--tools none` to skip tool setup.
|
||||
|
||||
## Commands don't show up
|
||||
|
||||
If `/opsx:propose` (or your tool's equivalent) doesn't appear or doesn't do anything, work down this list. They're ordered fastest-to-check first.
|
||||
|
||||
1. **You may be in the wrong place.** Slash commands go in your AI assistant's chat, not your terminal. If you typed `/opsx:propose` into your shell, that's the issue. See [How Commands Work](how-commands-work.md).
|
||||
|
||||
2. **Regenerate the files.** From your project root:
|
||||
|
||||
```bash
|
||||
openspec update
|
||||
```
|
||||
|
||||
This rewrites the skill and command files for every tool you've configured.
|
||||
|
||||
3. **Restart your assistant.** Most tools scan for skills and commands at startup. A fresh window often does it.
|
||||
|
||||
4. **Confirm the files exist.** For Claude Code, check that `.claude/skills/` contains `openspec-*` folders. Other tools use their own directories, all listed in [Supported Tools](supported-tools.md).
|
||||
|
||||
5. **Check you initialized this project.** Skills are written per project. If you cloned a repo or switched folders, run `openspec init` (or `openspec update`) there.
|
||||
|
||||
6. **Confirm your tool supports command files.** A few tools (Kimi CLI, ForgeCode, Mistral Vibe) don't get generated `opsx-*` command files; they use skill-based invocations instead. The forms differ per tool: see [Supported Tools](supported-tools.md) and [How Commands Work](how-commands-work.md#slash-command-syntax-by-tool).
|
||||
|
||||
## Working with changes
|
||||
|
||||
### "Change not found"
|
||||
|
||||
The command couldn't tell which change you meant. Name it explicitly, or check what exists:
|
||||
|
||||
```bash
|
||||
openspec list # see active changes
|
||||
/opsx:apply add-dark-mode # name the change in chat
|
||||
```
|
||||
|
||||
Also confirm you're in the right project directory.
|
||||
|
||||
### "No artifacts ready"
|
||||
|
||||
Every artifact is either already created or blocked waiting on a dependency. See what's blocking:
|
||||
|
||||
```bash
|
||||
openspec status --change <name>
|
||||
```
|
||||
|
||||
Then create the missing dependency first. Remember the order: proposal enables specs and design; specs and design together enable tasks.
|
||||
|
||||
### `openspec validate` reports warnings or errors
|
||||
|
||||
Validation checks your specs and changes for structural problems. Read the message: it names the file and the issue.
|
||||
|
||||
```bash
|
||||
openspec validate <name> # validate one item
|
||||
openspec validate --all # validate everything
|
||||
openspec validate --all --strict # stricter checks, good for CI
|
||||
```
|
||||
|
||||
Common causes are a missing required section (like a spec with no scenarios) or a malformed delta header. Fix the file and re-run. The [CLI reference](cli.md#openspec-validate) documents the output format.
|
||||
|
||||
### The AI created incomplete or wrong artifacts
|
||||
|
||||
The AI didn't have enough context. A few levers help:
|
||||
|
||||
- Add project context in `openspec/config.yaml` so your stack and conventions are injected into every request. See [Customization](customization.md#project-configuration).
|
||||
- Add per-artifact `rules:` for guidance that only applies to, say, specs.
|
||||
- Give a more detailed description when you propose.
|
||||
- Use the expanded `/opsx:continue` to create one artifact at a time and review each, instead of `/opsx:ff` doing them all at once.
|
||||
|
||||
### Archive won't finish, or warns about incomplete tasks
|
||||
|
||||
Archive won't *block* on incomplete tasks, but it warns you, because archiving usually means the work is done. If tasks remain on purpose (you're filing a partial change), proceed. Otherwise finish the tasks first. Archive will also offer to sync your delta specs into the main specs if you haven't synced yet; say yes unless you have a reason not to.
|
||||
|
||||
## Configuration
|
||||
|
||||
### My `config.yaml` isn't being applied
|
||||
|
||||
Three usual suspects:
|
||||
|
||||
1. **Wrong filename.** It must be `openspec/config.yaml`, not `.yml`.
|
||||
2. **Invalid YAML.** Run it through any YAML validator; the CLI also reports syntax errors with line numbers.
|
||||
3. **You expected a restart.** You don't need one. Config changes take effect immediately.
|
||||
|
||||
### "Unknown artifact ID in rules: X"
|
||||
|
||||
A key under `rules:` doesn't match any artifact in your schema. For the default `spec-driven` schema the valid IDs are `proposal`, `specs`, `design`, `tasks`. To see the IDs for any schema:
|
||||
|
||||
```bash
|
||||
openspec schemas --json
|
||||
```
|
||||
|
||||
### "Context too large"
|
||||
|
||||
The `context:` field is capped at 50KB, on purpose, because it's injected into every request. Summarize it, or link out to longer docs instead of pasting them. Lean context also produces better, faster results.
|
||||
|
||||
### "Schema not found"
|
||||
|
||||
The schema name you referenced doesn't exist. List what's available and check spelling:
|
||||
|
||||
```bash
|
||||
openspec schemas # list available schemas
|
||||
openspec schema which <name> # see where a schema resolves from
|
||||
openspec schema init <name> # create a custom one
|
||||
```
|
||||
|
||||
See [Customization](customization.md#custom-schemas).
|
||||
|
||||
## Migration from the legacy workflow
|
||||
|
||||
### "Legacy files detected in non-interactive mode"
|
||||
|
||||
You're in CI or a non-interactive shell, and OpenSpec found old files to clean up but can't prompt you. Approve automatically:
|
||||
|
||||
```bash
|
||||
openspec init --force
|
||||
```
|
||||
|
||||
### Commands didn't appear after migrating
|
||||
|
||||
Restart your IDE. Skills are detected at startup. If they still don't appear, run `openspec update` and check the file locations in [Supported Tools](supported-tools.md).
|
||||
|
||||
### My old `project.md` wasn't migrated
|
||||
|
||||
That's intentional. OpenSpec never deletes `project.md` automatically because it may hold context you wrote. Move the useful parts into `config.yaml`'s `context:` section, then delete it yourself. The [Migration Guide](migration-guide.md#migrating-projectmd-to-configyaml) walks through this, including a prompt you can hand to your AI to do the distilling.
|
||||
|
||||
## Still stuck?
|
||||
|
||||
- **Discord:** [discord.gg/YctCnvvshC](https://discord.gg/YctCnvvshC)
|
||||
- **GitHub Issues:** [github.com/Fission-AI/OpenSpec/issues](https://github.com/Fission-AI/OpenSpec/issues)
|
||||
- **From your terminal:** `openspec feedback "what went wrong"` opens an issue for you.
|
||||
|
||||
When you report a problem, include your OpenSpec version (`openspec --version`), your Node version (`node --version`), your AI tool, and the exact command and output. It makes help much faster.
|
||||
+65
-8
@@ -28,7 +28,60 @@ OPSX (fluid actions):
|
||||
|
||||
> **Customization:** OPSX workflows are driven by schemas that define artifact sequences. See [Customization](customization.md) for details on creating custom schemas.
|
||||
|
||||
## Workflow Patterns
|
||||
## Two Modes
|
||||
|
||||
### Default Quick Path (`core` profile)
|
||||
|
||||
New installs default to `core`, which provides:
|
||||
- `/opsx:explore`
|
||||
- `/opsx:propose`
|
||||
- `/opsx:apply`
|
||||
- `/opsx:sync`
|
||||
- `/opsx:archive`
|
||||
|
||||
Typical flow:
|
||||
|
||||
```text
|
||||
/opsx:explore ──► /opsx:propose ──► /opsx:apply ──► /opsx:sync ──► /opsx:archive
|
||||
(optional)
|
||||
```
|
||||
|
||||
#### Start by exploring (the habit worth forming)
|
||||
|
||||
`/opsx:explore` is part of the default profile, not an advanced add-on. It's the move to make whenever you have a problem but not yet a plan, which, with an AI assistant, is most of the time.
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: Our search results feel stale. I'm not sure if it's caching,
|
||||
indexing, or the query itself.
|
||||
|
||||
AI: Let me look... [reads the search service and cache layer]
|
||||
It's the index: it only rebuilds nightly, so edits don't show
|
||||
up until the next day. Two options: rebuild on write (simple,
|
||||
more load) or a short-TTL cache invalidation (more moving parts,
|
||||
less load). Given your write volume is low, rebuild-on-write is
|
||||
the cleaner fix. Want to scope it?
|
||||
|
||||
You: Yes.
|
||||
|
||||
You: /opsx:propose rebuild-search-index-on-write
|
||||
```
|
||||
|
||||
Explore creates no artifacts and writes no code. It's a free, no-stakes conversation that turns a vague worry into a precise change, so the proposal that follows is sharp. Already know exactly what you want? Skip it and go straight to `/opsx:propose`. Full guide: [Explore First](explore.md).
|
||||
|
||||
### Expanded/Full Workflow (custom selection)
|
||||
|
||||
If you want explicit scaffold-and-build commands (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), enable them with:
|
||||
|
||||
```bash
|
||||
openspec config profile
|
||||
openspec update
|
||||
```
|
||||
|
||||
## Workflow Patterns (Expanded Mode)
|
||||
|
||||
### Quick Feature
|
||||
|
||||
@@ -408,18 +461,22 @@ For full command details and options, see [Commands](commands.md).
|
||||
|
||||
| Command | Purpose | When to Use |
|
||||
|---------|---------|-------------|
|
||||
| `/opsx:explore` | Think through ideas | Unclear requirements, investigation |
|
||||
| `/opsx:new` | Start a change | Beginning any new work |
|
||||
| `/opsx:continue` | Create next artifact | Step-by-step artifact creation |
|
||||
| `/opsx:ff` | Create all planning artifacts | Clear scope, ready to build |
|
||||
| `/opsx:propose` | Create change + planning artifacts | Fast default path (`core` profile) |
|
||||
| `/opsx:explore` | Think through ideas with the AI | Start here when unsure: unclear requirements, investigation, comparing options |
|
||||
| `/opsx:new` | Start a change scaffold | Expanded mode, explicit artifact control |
|
||||
| `/opsx:continue` | Create next artifact | Expanded mode, step-by-step artifact creation |
|
||||
| `/opsx:ff` | Create all planning artifacts | Expanded mode, clear scope |
|
||||
| `/opsx:apply` | Implement tasks | Ready to write code |
|
||||
| `/opsx:verify` | Validate implementation | Before archiving, catch mismatches |
|
||||
| `/opsx:sync` | Merge delta specs | Optional—archive prompts if needed |
|
||||
| `/opsx:verify` | Validate implementation | Expanded mode, before archiving |
|
||||
| `/opsx:sync` | Merge delta specs | Expanded mode, optional |
|
||||
| `/opsx:archive` | Complete the change | All work finished |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes | Parallel work, batch completion |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes | Expanded mode, parallel work |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Writing Good Specs](writing-specs.md) - What a strong requirement and scenario look like, and how to right-size a change
|
||||
- [Reviewing a Change](reviewing-changes.md) - The two-minute pass on a drafted plan before any code
|
||||
- [OpenSpec on a Team](team-workflow.md) - How changes fit branches and pull requests
|
||||
- [Commands](commands.md) - Full command reference with options
|
||||
- [Concepts](concepts.md) - Deep dive into specs, artifacts, and schemas
|
||||
- [Customization](customization.md) - Create custom workflows
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# Writing Good Specs
|
||||
|
||||
You rarely write a spec from a blank page. You describe a change in plain language, `/opsx:propose` drafts the requirements and scenarios, and then you make them good. This page is about that last part — what "good" looks like, and how to steer the AI toward it.
|
||||
|
||||
It's the companion to [Reviewing a Change](reviewing-changes.md): reviewing is catching the weak spots in a draft, writing is knowing what a strong one is made of.
|
||||
|
||||
## A spec is behavior, not code
|
||||
|
||||
A spec says what your system *does*, in terms anyone could check — not how it's built. It's made of **requirements** (statements of behavior) and **scenarios** (concrete examples that prove them).
|
||||
|
||||
```markdown
|
||||
### Requirement: Session Timeout
|
||||
The system SHALL expire a session after 30 minutes of inactivity.
|
||||
|
||||
#### Scenario: Idle timeout
|
||||
- GIVEN an authenticated session
|
||||
- WHEN 30 minutes pass with no activity
|
||||
- THEN the session is invalidated and the user must re-authenticate
|
||||
```
|
||||
|
||||
Keep the *how* — the queue, the library, the table schema — in `design.md` or the code. When behavior and implementation get mixed into one requirement, the requirement stops being testable and starts going stale the moment the code changes.
|
||||
|
||||
## What makes a good requirement
|
||||
|
||||
A good requirement is one behavior, stated so plainly you could hand it to someone else to test.
|
||||
|
||||
- **One statement, one `SHALL`/`MUST`.** If a requirement has three "and also" clauses, it's really three requirements. Split them.
|
||||
- **Observable.** Someone outside the code should be able to tell whether it holds. "The system SHALL show an error banner when the upload exceeds 10 MB" is observable. "The system SHALL handle large uploads gracefully" is not.
|
||||
- **The right strength.** OpenSpec uses the RFC 2119 keywords, and they mean different things:
|
||||
|
||||
| Keyword | Meaning |
|
||||
|---------|---------|
|
||||
| `MUST` / `SHALL` | A hard requirement. Non-negotiable. |
|
||||
| `SHOULD` | A strong recommendation, with room for a justified exception. |
|
||||
| `MAY` | Genuinely optional. |
|
||||
|
||||
Reach for `MUST`/`SHALL` by default. Use `SHOULD` only when you truly mean "unless there's a good reason not to."
|
||||
|
||||
The test for a requirement: *could a tester who's never seen the code tell whether it passed?* If not, it needs sharpening.
|
||||
|
||||
## What makes a good scenario
|
||||
|
||||
Scenarios are where a requirement earns its keep. Each one is a concrete GIVEN / WHEN / THEN that could become an automated test.
|
||||
|
||||
- **It exercises its requirement.** A scenario that just restates the requirement in other words tests nothing. Make it a specific situation with a specific outcome.
|
||||
- **Cover the cases that matter, not just the happy path.** The valid login is easy. The empty input, the expired token, the second click, the thing that goes wrong — those are where bugs live, and where a scenario is worth the most.
|
||||
- **Name the case in the title.** "Scenario: Rejects an expired token" tells a reviewer what's covered at a glance; "Scenario: Test 2" doesn't.
|
||||
|
||||
A useful habit: before approving, ask *what's the one case I'd be upset to see broken?* — and make sure a scenario names it.
|
||||
|
||||
## Pick the right kind of delta
|
||||
|
||||
A change describes its edits to the specs with three section types. Using the right one keeps your archived specs honest:
|
||||
|
||||
- **`## ADDED Requirements`** — brand-new behavior that didn't exist before.
|
||||
- **`## MODIFIED Requirements`** — behavior that already existed and is changing. Include the full new version; a short note on what changed helps a reviewer.
|
||||
- **`## REMOVED Requirements`** — behavior going away, with a line on why.
|
||||
|
||||
On archive, ADDED gets appended to the main spec, MODIFIED replaces the old version, and REMOVED is deleted. If you mark a real change as ADDED, you end up with two competing requirements; if you describe new behavior as MODIFIED, there's nothing to replace. When in doubt, open the current spec and see whether the requirement is already there.
|
||||
|
||||
## Right-size the change
|
||||
|
||||
The single most common authoring mistake isn't a badly worded requirement — it's a change that's trying to be three changes.
|
||||
|
||||
**A good change has one intent you can say in a sentence.** "Add a dark-mode toggle." "Rate-limit the login endpoint." "Migrate sessions off cookies." If describing the change needs a lot of "and also," that's the signal to split it.
|
||||
|
||||
Signs a change is too big:
|
||||
|
||||
- The proposal's scope reads like a list of unrelated features.
|
||||
- Reviewing it would take an afternoon, so nobody will.
|
||||
- Two people couldn't work on it without colliding.
|
||||
- Half the tasks could ship on their own.
|
||||
|
||||
Smaller changes are easier to review, easier to build in one focused session, and easier to reason about six months later when the archive is all that's left. You can always run several changes in parallel — see [Editing & iterating](editing-changes.md) and [Workflows](workflows.md).
|
||||
|
||||
The opposite also happens: a one-line typo fix doesn't need three requirements and a design doc. Match the ceremony to the stakes.
|
||||
|
||||
## How to steer the AI toward a good draft
|
||||
|
||||
Because `/opsx:propose` does the first draft, the quality of what you get back tracks the quality of what you give it. You don't have to write requirements by hand — you have to aim the AI well:
|
||||
|
||||
- **State the intent and the boundary.** *"Add a dark-mode toggle that follows the OS setting on first load — don't touch the existing theme API."* The out-of-scope half matters as much as the in-scope half.
|
||||
- **Name the cases you care about.** *"Make sure there's a scenario for a user who already picked a theme manually."* The AI covers what you point at.
|
||||
- **Then edit.** It's plain Markdown. Tighten a vague `SHALL`, delete a scenario that tests nothing, add the case it missed — or ask the AI to: *"the timeout requirement is vague, pin it to 30 minutes."*
|
||||
|
||||
Draft, sharpen, repeat. A few rounds of that produces a spec you'd trust, which is the whole point.
|
||||
|
||||
## A quick checklist
|
||||
|
||||
- [ ] Each requirement is one observable behavior with a `SHALL`/`MUST`.
|
||||
- [ ] No implementation details are baked into the requirements.
|
||||
- [ ] Every requirement has at least one scenario that actually exercises it.
|
||||
- [ ] The important edge and error cases have scenarios, not just the happy path.
|
||||
- [ ] Deltas use ADDED / MODIFIED / REMOVED correctly against the current spec.
|
||||
- [ ] The whole change has one intent you can state in a sentence.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Reviewing a Change](reviewing-changes.md) — the two-minute pass that catches what slipped through.
|
||||
- [Concepts](concepts.md) — the deeper model behind specs, changes, and deltas.
|
||||
- [Examples & Recipes](examples.md) — real changes from start to finish.
|
||||
@@ -51,7 +51,7 @@
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = pkgs.pnpm_9;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-9s2kdvd7svK4hofnD66HkDc86WTQeayfF5y7L2dmjNg=";
|
||||
hash = "sha256-cFY6phUPK4IOthG/aOtMenyQlLYCCilcOIG+G+v/q04=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
# Add Artifact Regeneration Support
|
||||
|
||||
## Problem
|
||||
|
||||
Currently, there is **no way to regenerate artifacts** in the OPSX workflow:
|
||||
|
||||
- `/opsx:apply` just reads whatever's on disk
|
||||
- `/opsx:continue` only creates the NEXT artifact - won't touch existing ones
|
||||
|
||||
If you edit `design.md` after `tasks.md` exists, your only options are:
|
||||
1. Delete tasks.md manually, then run `/opsx:continue`
|
||||
2. Edit tasks.md manually
|
||||
|
||||
The documentation claims you can "update artifacts mid-flight and continue" but there's no mechanism that actually supports this.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
Two parts:
|
||||
|
||||
### Part 1: Staleness Detection
|
||||
Add artifact staleness detection to `/opsx:apply`:
|
||||
|
||||
1. **Track modification times**: When generating an artifact, record the mtime of its dependencies
|
||||
2. **Detect staleness**: When `/opsx:apply` runs, check if upstream artifacts (design.md, specs) have been modified since tasks.md was generated
|
||||
3. **Prompt user**: If stale, ask: "Design was modified after tasks were generated. Would you like to regenerate tasks with `/opsx:continue`?"
|
||||
|
||||
## User Experience
|
||||
|
||||
### Vision: Seamless Mid-Flight Correction
|
||||
|
||||
This is the workflow we want to enable (currently documented but not supported):
|
||||
|
||||
```
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Working through tasks...
|
||||
✓ Task 1.1: Created caching layer
|
||||
✓ Task 1.2: Added cache invalidation
|
||||
|
||||
Working on 1.3: Implement TTL...
|
||||
I noticed the design assumes Redis, but your project uses
|
||||
in-memory caching. Should I update the design?
|
||||
|
||||
You: Yes, update it to use the existing cache module.
|
||||
|
||||
AI: Updated design.md to use CacheManager from src/cache/
|
||||
Updated tasks.md with revised implementation steps
|
||||
Continuing implementation...
|
||||
✓ Task 1.3: Implemented TTL using CacheManager
|
||||
...
|
||||
```
|
||||
|
||||
**No restart needed.** Just update the artifact and continue.
|
||||
|
||||
### Staleness Warning UX
|
||||
|
||||
When user manually edits an upstream artifact:
|
||||
|
||||
```
|
||||
$ /opsx:apply
|
||||
|
||||
⚠️ Detected changes to upstream artifacts:
|
||||
- design.md modified 5 minutes ago (after tasks.md was generated)
|
||||
|
||||
Options:
|
||||
1. Regenerate tasks (recommended)
|
||||
2. Continue anyway with current tasks
|
||||
3. Cancel
|
||||
|
||||
>
|
||||
```
|
||||
|
||||
### Part 2: Regeneration Capability
|
||||
|
||||
Add a way to regenerate specific artifacts:
|
||||
|
||||
```bash
|
||||
# Option A: Flag on continue
|
||||
/opsx:continue --regenerate tasks
|
||||
|
||||
# Option B: Separate command
|
||||
/opsx:regenerate tasks
|
||||
|
||||
# Option C: Interactive prompt when staleness detected
|
||||
/opsx:apply
|
||||
# "Design changed. Regenerate tasks? [y/N]"
|
||||
```
|
||||
|
||||
## Technical Approach
|
||||
|
||||
### Option A: Metadata File
|
||||
Store `.openspec-meta.json` in change directory:
|
||||
```json
|
||||
{
|
||||
"tasks.md": {
|
||||
"generated_at": "2025-01-24T10:00:00Z",
|
||||
"dependencies": {
|
||||
"design.md": "2025-01-24T09:55:00Z",
|
||||
"specs/feature/spec.md": "2025-01-24T09:50:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Option B: Frontmatter
|
||||
Add YAML frontmatter to generated artifacts:
|
||||
```markdown
|
||||
---
|
||||
generated_at: 2025-01-24T10:00:00Z
|
||||
depends_on:
|
||||
- design.md@2025-01-24T09:55:00Z
|
||||
---
|
||||
# Tasks
|
||||
...
|
||||
```
|
||||
|
||||
### Option C: Git-based
|
||||
Use git to detect if upstream files changed since downstream was last modified. No extra metadata needed but requires git.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Automatic regeneration (user should always choose)
|
||||
- Blocking apply entirely (just warn)
|
||||
- Tracking code file changes (only artifact dependencies)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Should be implemented after `fix-midflight-update-docs` so docs are accurate first
|
||||
- Could be combined with that change if desired
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- User is warned when applying with stale artifacts
|
||||
- Clear path to regenerate if needed
|
||||
- No false positives (only warn when genuinely stale)
|
||||
- Documentation claims become actually true
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Every generated OpenSpec skill drives the `openspec` CLI (`openspec list`, `status`, `instructions`, …). Today the skill frontmatter never pre-approves those calls, so agents that gate Bash on permission prompt the user on every single `openspec` invocation. The workflow stalls on approvals for a first-party, read-mostly CLI the user already opted into by installing OpenSpec.
|
||||
|
||||
The Agent Skills standard already solves this: an `allowed-tools` frontmatter field pre-approves listed tools while a skill is active. We just aren't emitting it.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Every generated `SKILL.md` gains `allowed-tools: Bash(openspec:*)` in its YAML frontmatter, so agents run `openspec` commands from the skill without prompting. Emitted centrally in `generateSkillContent`, so `init`, `update`, every tool's skills directory, and every current and future skill get it uniformly.
|
||||
- Claude Code slash commands (`.claude/commands/opsx/*.md`) gain the same field — commands share the skill frontmatter contract, so the same pre-approval applies when a user runs `/opsx:*`.
|
||||
- Scope is deliberately narrow: only the `openspec` CLI is pre-approved. Per the standard, `allowed-tools` pre-approves rather than restricts — so any other tool a skill or command uses (Read, Write, or arbitrary Bash for builds/tests in `apply`/`onboard`) stays available under the user's normal permission settings, still prompting as before.
|
||||
- Cross-tool: skills go to every supported tool's skills directory, and `allowed-tools` is an Agent Skills standard field — tools that implement the standard honor it; tools that don't ignore the unknown key. Only the Claude command adapter changes, because no other tool's slash-command format defines a per-command pre-approval field.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `cli-init`: the Skill Generation requirement now specifies the `allowed-tools` pre-approval in generated skill frontmatter.
|
||||
- `command-generation`: the Claude adapter frontmatter now includes the `allowed-tools` field.
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/shared/allowed-tools.ts` — the shared `OPENSPEC_CLI_ALLOWED_TOOLS` constant (single source for both surfaces).
|
||||
- `src/core/shared/skill-generation.ts` — emit `allowed-tools` in the SKILL.md frontmatter.
|
||||
- `src/core/command-generation/adapters/claude.ts` — emit `allowed-tools` in the slash-command frontmatter.
|
||||
- Tests: regenerated golden skill-content hashes; new assertions that every deployed skill and the Claude command format pre-approve the CLI.
|
||||
- No behavior change for agents that ignore `allowed-tools`; pure upside for agents that honor it.
|
||||
@@ -0,0 +1,28 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Skill Generation
|
||||
|
||||
The command SHALL generate Agent Skills for selected AI tools.
|
||||
|
||||
#### Scenario: Generating skills for a tool
|
||||
|
||||
- **WHEN** a tool is selected during initialization
|
||||
- **THEN** create 9 skill directories under `.<tool>/skills/`:
|
||||
- `openspec-explore/SKILL.md`
|
||||
- `openspec-new-change/SKILL.md`
|
||||
- `openspec-continue-change/SKILL.md`
|
||||
- `openspec-apply-change/SKILL.md`
|
||||
- `openspec-ff-change/SKILL.md`
|
||||
- `openspec-verify-change/SKILL.md`
|
||||
- `openspec-sync-specs/SKILL.md`
|
||||
- `openspec-archive-change/SKILL.md`
|
||||
- `openspec-bulk-archive-change/SKILL.md`
|
||||
- **AND** each SKILL.md SHALL contain YAML frontmatter with name and description
|
||||
- **AND** each SKILL.md SHALL contain the skill instructions
|
||||
|
||||
#### Scenario: Pre-approving the OpenSpec CLI in skill frontmatter
|
||||
|
||||
- **WHEN** generating a skill's YAML frontmatter
|
||||
- **THEN** the frontmatter SHALL include an `allowed-tools` field with the value `Bash(openspec:*)`
|
||||
- **AND** an agent that honors `allowed-tools` SHALL run `openspec` commands from the skill without prompting for approval
|
||||
- **AND** because `allowed-tools` pre-approves rather than restricts, any other tool the skill uses SHALL remain available under the user's existing permission settings
|
||||
@@ -0,0 +1,32 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: ToolCommandAdapter interface
|
||||
|
||||
The system SHALL define a `ToolCommandAdapter` interface for per-tool formatting.
|
||||
|
||||
#### Scenario: Adapter interface structure
|
||||
|
||||
- **WHEN** implementing a tool adapter
|
||||
- **THEN** `ToolCommandAdapter` SHALL require:
|
||||
- `toolId`: string identifier matching `AIToolOption.value`
|
||||
- `getFilePath(commandId: string)`: returns file path for command (relative from project root, or absolute for global-scoped tools like Codex)
|
||||
- `formatFile(content: CommandContent)`: returns complete file content with frontmatter
|
||||
|
||||
#### Scenario: Claude adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Claude Code
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `allowed-tools`, `category`, `tags` fields
|
||||
- **AND** the `allowed-tools` field SHALL have the value `Bash(openspec:*)` so Claude Code runs `openspec` commands from the slash command without prompting for approval
|
||||
- **AND** file path SHALL follow pattern `.claude/commands/opsx/<id>.md`
|
||||
|
||||
#### Scenario: Cursor adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Cursor
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name` as `/opsx-<id>`, `id`, `category`, `description` fields
|
||||
- **AND** file path SHALL follow pattern `.cursor/commands/opsx-<id>.md`
|
||||
|
||||
#### Scenario: Windsurf adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Windsurf
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields
|
||||
- **AND** file path SHALL follow pattern `.windsurf/workflows/opsx-<id>.md`
|
||||
@@ -0,0 +1,15 @@
|
||||
## 1. Implementation
|
||||
|
||||
- [x] 1.1 Add the shared `OPENSPEC_CLI_ALLOWED_TOOLS = 'Bash(openspec:*)'` constant (`src/core/shared/allowed-tools.ts`) and emit `allowed-tools` in the frontmatter built by `generateSkillContent`
|
||||
- [x] 1.2 Emit the same `allowed-tools` field in the Claude command adapter's frontmatter (`src/core/command-generation/adapters/claude.ts`); other adapters unchanged — no other tool defines a per-command pre-approval field
|
||||
|
||||
## 2. Tests
|
||||
|
||||
- [x] 2.1 Regenerate the golden generated-content hashes in `skill-templates-parity.test.ts`
|
||||
- [x] 2.2 Add a test asserting every deployed skill's generated content contains `allowed-tools: Bash(openspec:*)` (iterates the registry so new skills are covered)
|
||||
- [x] 2.3 Assert the Claude adapter output contains the field (`adapters.test.ts`)
|
||||
- [x] 2.4 Verify end-to-end: `openspec init --tools claude` emits the field in both SKILL.md and `.claude/commands/opsx/*.md`, and it parses as the YAML string `Bash(openspec:*)`
|
||||
|
||||
## 3. Release
|
||||
|
||||
- [x] 3.1 Add a changeset describing the auto-approval
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
OpenSpec currently assumes command delivery maps directly to command adapters. That assumption does not hold for all tools.
|
||||
|
||||
Trae is a concrete example: it invokes OpenSpec workflows via skill entries (for example `/openspec-new-change`) rather than adapter-generated command files. In this model, skills are the command surface.
|
||||
Some tools expose OpenSpec workflows via skill entries rather than adapter-generated command files. Kimi CLI is a concrete example: it invokes skills with forms such as `/skill:openspec-new-change`. In this model, skills are the command surface.
|
||||
|
||||
Today, this creates a behavior gap:
|
||||
|
||||
- `delivery=commands` can remove skills
|
||||
- tools without adapters skip command generation
|
||||
- result: selected tools like Trae can end up with no invocable workflow artifacts
|
||||
- result: selected tools like Kimi CLI, ForgeCode, or Mistral Vibe can end up with no invocable workflow artifacts
|
||||
|
||||
This is more than a prompt UX issue because non-interactive and CI flows bypass interactive guidance. We need a capability-aware model in core generation logic.
|
||||
|
||||
@@ -25,9 +25,13 @@ Add an optional field in tool metadata to describe how a tool exposes commands:
|
||||
Field should be optional. Default behavior is inferred from adapter registry presence: tools with a registered adapter resolve to `adapter`; tools with no adapter registration and no explicit annotation resolve to `none`.
|
||||
Capability values use kebab-case string tokens for consistency with serialized metadata conventions.
|
||||
|
||||
Initial explicit override:
|
||||
Initial explicit overrides:
|
||||
|
||||
- Trae -> `skills-invocable`
|
||||
- ForgeCode -> `skills-invocable`
|
||||
- Kimi CLI -> `skills-invocable`
|
||||
- Mistral Vibe -> `skills-invocable`
|
||||
|
||||
Trae no longer belongs in this override set once its `.trae/commands/opsx-<id>.md` adapter is available; it should resolve to `adapter` like other file-backed command integrations.
|
||||
|
||||
### 2. Make delivery behavior capability-aware
|
||||
|
||||
@@ -62,12 +66,12 @@ Update summaries to show effective delivery outcomes per tool (for example, when
|
||||
|
||||
### 4. Update docs and tests
|
||||
|
||||
- document capability model and Trae behavior under delivery modes
|
||||
- document capability model and skills-invocable behavior under delivery modes
|
||||
- ensure CLI docs and supported-tools docs reflect effective behavior
|
||||
- add test coverage for:
|
||||
- `init --tools trae` with `delivery=commands`
|
||||
- `update` with Trae configured under `delivery=commands`
|
||||
- mixed selections (`claude + trae`) across all delivery modes
|
||||
- `init --tools kimi` with `delivery=commands`
|
||||
- `update` with Kimi CLI configured under `delivery=commands`
|
||||
- mixed selections (`claude + kimi`) across all delivery modes
|
||||
- explicit error path for tools with no command surface under `delivery=commands`
|
||||
|
||||
### 5. Coordinate with install-scope behavior
|
||||
@@ -94,7 +98,7 @@ Implementation tests should cover mixed-tool matrices to ensure deterministic be
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/config.ts` - add optional command-surface metadata and Trae override
|
||||
- `src/core/config.ts` - add optional command-surface metadata and skills-invocable tool overrides
|
||||
- `src/core/command-generation/registry.ts` (or shared helper) - capability inference from adapter presence
|
||||
- `src/core/init.ts` - capability-aware generation/removal planning + compatibility validation + summary messaging
|
||||
- `src/core/update.ts` - capability-aware sync/removal planning + compatibility validation + summary messaging
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
- [ ] 1.1 Extend tool metadata in `src/core/config.ts` with an optional command-surface capability field
|
||||
- [ ] 1.2 Define supported capability values: `adapter`, `skills-invocable`, `none`
|
||||
- [ ] 1.3 Mark Trae as `skills-invocable`
|
||||
- [ ] 1.3 Mark known skills-invocable tools such as ForgeCode, Kimi CLI, and Mistral Vibe as `skills-invocable`
|
||||
- [ ] 1.4 Add a shared capability resolver (explicit metadata override first, inferred fallback from adapter presence second)
|
||||
- [ ] 1.5 Add focused unit tests for capability resolution (explicit override, inferred adapter, inferred none)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
- [ ] 2.3 In `delivery=commands`, fail fast before writes when any selected tool resolves to `none`
|
||||
- [ ] 2.4 Update init output to clearly report effective behavior for `skills-invocable` tools (skills used as command surface)
|
||||
- [ ] 2.5 Ensure init no longer reports "no adapter" for tools intentionally using `skills-invocable`
|
||||
- [ ] 2.6 Add/adjust init tests for `delivery=commands` + `trae` (skills retained/generated, no adapter error), mixed tools (`claude,trae`) with per-tool expected outputs, and deterministic failure path for unsupported command surface (`none`)
|
||||
- [ ] 2.6 Add/adjust init tests for `delivery=commands` + `kimi` (skills retained/generated, no adapter error), mixed tools (`claude,kimi`) with per-tool expected outputs, and deterministic failure path for unsupported command surface (`none`)
|
||||
|
||||
## 3. Update: Capability-Aware Sync and Drift Detection
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
- [ ] 3.4 Update profile/delivery drift detection to avoid perpetual drift for `skills-invocable` tools under commands delivery
|
||||
- [ ] 3.5 Ensure configured-tool detection still includes `skills-invocable` tools under commands delivery when managed skills exist
|
||||
- [ ] 3.6 Update summary output so skills-invocable behavior is reported as expected behavior (not implicit skip/error)
|
||||
- [ ] 3.7 Add/adjust update tests for `delivery=commands` + configured Trae (skills retained/generated), idempotent second update (no false drift loop), mixed configured tools (`claude` + `trae`), and deterministic preflight failure for unsupported command surface (`none`)
|
||||
- [ ] 3.7 Add/adjust update tests for `delivery=commands` + configured Kimi CLI (skills retained/generated), idempotent second update (no false drift loop), mixed configured tools (`claude` + `kimi`), and deterministic preflight failure for unsupported command surface (`none`)
|
||||
|
||||
## 4. UX and Error Messaging
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
## 5. Documentation Updates
|
||||
|
||||
- [ ] 5.1 Update `docs/supported-tools.md` to document command-surface semantics for Trae and clarify delivery interactions
|
||||
- [ ] 5.1 Update `docs/supported-tools.md` to document command-surface semantics for skills-invocable tools and clarify delivery interactions
|
||||
- [ ] 5.2 Update `docs/cli.md` delivery guidance to explain capability-aware behavior for `delivery=commands`
|
||||
- [ ] 5.3 Add a short troubleshooting note for "commands-only + unsupported tool" failures
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
- [ ] 6.1 Run targeted tests: `test/core/init.test.ts` and `test/core/update.test.ts`
|
||||
- [ ] 6.2 Run any new capability/unit test files added in this change
|
||||
- [ ] 6.3 Run full test suite (`pnpm test`) and resolve regressions
|
||||
- [ ] 6.4 Manual smoke check: `openspec init --tools trae` with `delivery=commands`
|
||||
- [ ] 6.5 Manual smoke check: mixed tools (`claude,trae`) with `delivery=commands`
|
||||
- [ ] 6.4 Manual smoke check: `openspec init --tools kimi` with `delivery=commands`
|
||||
- [ ] 6.5 Manual smoke check: mixed tools (`claude,kimi`) with `delivery=commands`
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-06-29
|
||||
@@ -0,0 +1,115 @@
|
||||
# Design: `/opsx:update` — a thin update skill
|
||||
|
||||
## Context
|
||||
|
||||
OPSX models a change as a small DAG of planning artifacts. Each schema declares artifacts with `requires` edges ([schemas/spec-driven/schema.yaml](../../../schemas/spec-driven/schema.yaml)); `ArtifactGraph` ([src/core/artifact-graph/graph.ts](../../../src/core/artifact-graph/graph.ts)) topologically sorts them, and `openspec status --change <id> --json` already reports, per artifact: its `status` (`done`/`ready`/`blocked`), its `outputPath`, and — via the top-level `artifactPaths` map — its `resolvedOutputPath` and `existingOutputPaths`, plus the change's `schemaName` and `isComplete`. The two path fields differ in a way that matters for a write operation: `existingOutputPaths` is the concrete files that exist on disk (for a glob artifact such as `specs/**/*.md`, the glob already expanded to real files); `resolvedOutputPath` is the change-dir-joined declared path, which for a glob artifact is still the glob (`.../specs/**/*.md`) and is therefore **not** a write target. `/opsx:update` edits the files in `existingOutputPaths`. `openspec list --json` lists changes by recency.
|
||||
|
||||
That is everything an update skill needs. The artifacts are a handful of markdown files on disk; the agent can read them. So `/opsx:update` is built as a thin skill over the **existing** CLI, in the same shape as `continue-change.ts` (select change → `openspec status --json` → act).
|
||||
|
||||
This proposal began larger — a reverse-dependency graph API, content digests, a baseline ledger, a `reconcile` write op, a `status --impact` selector. Review feedback ([PR #1278](https://github.com/Fission-AI/OpenSpec/pull/1278)) was that this over-builds: coding agents tend to over-complicate skills, and the feature should work off the existing `status` command with as little new code as possible. This design follows that steer.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**
|
||||
- A `/opsx:update` action that revises a change's existing planning artifacts and keeps them coherent with one another.
|
||||
- Drive it from the artifact set and paths the CLI already reports — zero hardcoded artifact names — so custom schemas work.
|
||||
- Edit planning artifacts only; never touch code. Confirm every edit with the user.
|
||||
- Add as little code as possible: one skill template, no changes to the graph engine, the `status` command, or the metadata schema.
|
||||
|
||||
**Non-Goals**
|
||||
- A new top-level `openspec update*` CLI verb (name is taken; see Naming).
|
||||
- Automatic, unattended regeneration (the user always confirms).
|
||||
- Content digests, a drift/staleness signal, a baseline ledger, a `reconcile` op, or a `status --impact` selector (see "Why not the heavier machinery").
|
||||
- Regenerating *code* from updated artifacts — that is `/opsx:apply`'s job; `/opsx:update` stops at the plan and hands off.
|
||||
- Cross-change audit ([#247](https://github.com/Fission-AI/OpenSpec/issues/247) in full) — a later proposal; this change is intra-change.
|
||||
- Updating anything other than a change's planning artifacts. v1 is specific to change proposals; generalizing "update" to other graph types is deferred until such a graph exists (see Naming).
|
||||
|
||||
## The skill, written by hand
|
||||
|
||||
Working backwards from "what is the minimal instruction set," here is the skill body in sketch form. It is short on purpose — few tokens, few commands:
|
||||
|
||||
```
|
||||
Revise a change's planning artifacts and keep them coherent. Never edit code.
|
||||
|
||||
1. Resolve the change.
|
||||
- If named, use it. Else infer from context; if unclear, run `openspec list --json`
|
||||
and ask the user to choose (most-recently-modified first). Never auto-select.
|
||||
|
||||
2. Get the artifacts.
|
||||
- Run `openspec status --change "<id>" --json`.
|
||||
- Read `artifacts[]` (ids + status) and the `artifactPaths` map. These come from the
|
||||
active schema — do not assume the artifact ids or paths.
|
||||
- The files to edit are `artifactPaths.<id>.existingOutputPaths` (already glob-expanded
|
||||
for artifacts like `specs/**/*.md`). Do not write to `resolvedOutputPath`: for a glob
|
||||
artifact it is still the glob pattern, not a real file.
|
||||
|
||||
3. Understand the request.
|
||||
- If the user named a change ("the design now uses X"), that is the starting edit.
|
||||
- If they only said "update" / "make this coherent," treat it as a coherence review.
|
||||
|
||||
4. Read and reconcile.
|
||||
- Read the artifact(s) the request touches and the other existing artifacts in the change.
|
||||
- Apply the requested edit. Then check every other existing artifact against it — in any
|
||||
direction (an edit to design may require revising the proposal, not only the tasks) —
|
||||
and note what is now inconsistent, missing, or contradictory.
|
||||
- Do not invent artifacts that don't exist yet; point the user to `/opsx:continue` to create them.
|
||||
|
||||
5. Confirm and apply, one artifact at a time.
|
||||
- Show each proposed revision and why. Write only after the user confirms.
|
||||
- When a substantial rewrite is needed, `openspec instructions <artifact> --change "<id>" --json`
|
||||
gives that artifact's rules/template to follow.
|
||||
|
||||
6. Point to the next step (guidance only — never act on it).
|
||||
- Artifacts still missing → suggest `/opsx:continue`. Change already implemented (tasks
|
||||
checked off / applied) → the code may no longer match the revised plan; suggest
|
||||
`/opsx:apply` to carry the delta. Fully done and implemented → suggest `/opsx:archive`.
|
||||
|
||||
Guardrails:
|
||||
- Planning artifacts only. If the plan now implies code changes, stop and point to `/opsx:apply`.
|
||||
- Use artifact ids/paths from `openspec status`; never branch on literal proposal/specs/design/tasks names.
|
||||
- If the request changes the change's *intent* rather than refining it, recommend `/opsx:new`
|
||||
(the "Update vs. Start Fresh" heuristic, docs/opsx.md).
|
||||
```
|
||||
|
||||
The `spec-driven` artifact names may appear once, as a worked *example* of how to apply step 4, exactly as `continue-change.ts` does today — but the control flow reads ids from the CLI, so the skill never branches on those names. A template test asserts there is no name-based branching (the anti-[#777](https://github.com/Fission-AI/OpenSpec/issues/777) guard).
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Bidirectional coherence, not downstream propagation
|
||||
The artifact graph has a build *order*, but "what needs updating after an edit" is not strictly downstream. If `design` changes, the `proposal` it elaborates may need to change too; if `tasks` reveal a missing capability, the `specs` may need a new requirement. The skill therefore reads the change's artifacts and reconciles them in whatever direction the edit demands. Build order is still useful as a default *reading* order and for presenting fixes, but it is not a constraint on which artifacts may be revised. This is why the design does not add a one-directional `getDownstream` / `--impact` primitive: it would encode the wrong model.
|
||||
|
||||
### 2. Lean on the existing `status` command
|
||||
`openspec status --change <id> --json` already returns the artifact set, per-artifact status, and, in the `artifactPaths` map, the on-disk paths. The skill writes to `artifactPaths.<id>.existingOutputPaths` — the concrete files, glob-expanded — and deliberately not to `resolvedOutputPath`, which for a glob artifact is the pattern itself and not a file. That is everything the skill needs to know what exists and where it lives; no new CLI field is required. Picking the change reuses `openspec list --json`, exactly like `/opsx:continue`. No new CLI surface is introduced.
|
||||
|
||||
### 3. Why not the heavier machinery (digests, ledger, reconcile, impact)
|
||||
The first draft proposed SHA-256 content digests, a per-change baseline ledger in `.openspec.yaml`, an `openspec reconcile` write op, a derived drift signal, and a `status --impact` selector — so the CLI could tell the agent *which* artifacts are stale without the agent reading them.
|
||||
|
||||
Rejected for v1, because the cost outweighs the need:
|
||||
- The artifacts are a few markdown files. An agent that is going to *rewrite* them must read them anyway, so computing staleness for it saves little and adds a stateful subsystem (a ledger that `status` must not mutate, a separate write verb, scheme-versioning for forward-compat, cross-platform digest canonicalization, and the round-trip tests for all of it).
|
||||
- A digest/ledger only earns its keep when something must judge staleness *without* reading content — e.g. unattended drift detection across many changes ([#247](https://github.com/Fission-AI/OpenSpec/issues/247) cross-change, [#846](https://github.com/Fission-AI/OpenSpec/issues/846) tracking files). Those are out of scope here. When one of them becomes concrete, this machinery can be designed against that real need.
|
||||
|
||||
So `/opsx:update` v1 has the agent read the change's artifacts and judge coherence directly. If, after using it, a deterministic signal proves necessary, the smallest first step is to expose the schema's `requires` edges on `status --json` (a single additive field, no new command) — and only then consider digests.
|
||||
|
||||
### 4. Naming: `/opsx:update` skill, not `openspec update` CLI
|
||||
`openspec update [path]` already regenerates AI tool/skill files ([src/cli/index.ts](../../../src/cli/index.ts)). Overloading it would give one verb two unrelated meanings. The artifact-update action is therefore the **skill** `/opsx:update`, with no new `openspec` verb at all. Considered and rejected: `openspec regen --from <artifact>` ([#705](https://github.com/Fission-AI/OpenSpec/issues/705)) — a mutating CLI verb that rewrites artifacts duplicates the skill's job and bypasses user confirmation; the value is in the agent's semantic revision, not a CLI rewrite.
|
||||
|
||||
Review feedback flagged that "update" alone is generic — could it apply to any graph? The resolution: the skill is scoped to **change proposals only**, and the specific name carries that scope. The skill is `openspec-update-change`, following the `openspec-<verb>-change` naming of its siblings (`openspec-continue-change`, `openspec-new-change`, …). The command is `/opsx:update` because every verb in the `/opsx:` family operates on a change (`continue`, `apply`, `archive` — none says `-change`); a change-scoped meaning is what the namespace already promises. If a future graph type needs its own update action, it gets its own specific skill name then — nothing here blocks or breaks that.
|
||||
|
||||
### 5. Guardrails (the part that makes it the requested command)
|
||||
- **Planning artifacts only.** The skill's write targets are the artifact paths from `status`; if a revision implies code changes it stops and points to `/opsx:apply`. This directly answers [#1188](https://github.com/Fission-AI/OpenSpec/issues/1188)'s complaint that the manual workaround edits code.
|
||||
- **Schema-driven.** Ids and paths come from `status`; no branching on literal `proposal`/`specs`/`design`/`tasks`. Works for custom schemas ([#777](https://github.com/Fission-AI/OpenSpec/issues/777), [#666](https://github.com/Fission-AI/OpenSpec/issues/666)).
|
||||
- **Confirm each edit.** One artifact at a time, shown before writing.
|
||||
- **Intent guard.** A revision that changes intent rather than refining it is redirected to `/opsx:new` (the "Update vs. Start Fresh" heuristic, [docs/opsx.md](../../../docs/opsx.md)).
|
||||
|
||||
### 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.
|
||||
|
||||
## 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).
|
||||
- **Coherence quality depends on the agent.** Mitigated by confirming every edit and by keeping scope to one change's artifacts (a small, readable set).
|
||||
- **Skill drifts back to hardcoding artifact names.** Mitigated by a template test asserting the control flow reads ids from `status` JSON and contains no name-based branching.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Additive and backward-compatible. One new skill template, installed with the default `core` profile (maintainer call on the PR: update is part of the default happy path, not expanded-only); one docs row. No existing command changes behavior; no schema or graph changes. The superseded stub (`add-artifact-regeneration-support`) is removed or folded in the same PR to avoid two competing proposals in the tree.
|
||||
@@ -0,0 +1,66 @@
|
||||
## Why
|
||||
|
||||
OPSX names **four** first-class actions — "create, implement, **update**, archive — do any of them anytime" ([docs/opsx.md:52](../../../docs/opsx.md)). Three ship as commands. **`update` does not exist.** The only mechanism offered is *"edit the files manually"* — and when you edit one artifact, nothing helps you keep the rest of the change coherent. Worse, the manual workaround lets the agent edit **code** when the user only wanted to revise the **plan** ([#1188](https://github.com/Fission-AI/OpenSpec/issues/1188)).
|
||||
|
||||
This is the most-requested missing capability in the tracker. It is one gap with several faces, and the fix is small: a thin `/opsx:update` skill that revises a change's planning artifacts and keeps them coherent with each other, built on the **existing** `openspec status` / `openspec list` commands. No new graph engine, no digests, no ledger — just an agent that reads the change's artifacts and updates what needs updating, with the user's confirmation.
|
||||
|
||||
## What Changes
|
||||
|
||||
The whole feature is a single new workflow skill, `/opsx:update`. The skill is deliberately change-scoped — `openspec-update-change`, following the `openspec-<verb>-change` naming of its siblings — and applies to change proposals only, not arbitrary artifact graphs (see design, Naming). Written by hand, its instruction set is short:
|
||||
|
||||
1. **Understand the request** — what the user wants to revise (or, with no specific ask, "review this change for coherence").
|
||||
2. **Get the artifacts** — run `openspec status --change <id> --json`. Its `artifactPaths` map reports, per artifact, which files exist and where: `existingOutputPaths` is the concrete file list to edit — already expanded for glob artifacts like `specs/**/*.md`. (`openspec list --json` to pick the change when it isn't given.)
|
||||
3. **Read and revise** — read the relevant artifacts, make the requested edit, then check the change's **other** artifacts against it and propose any follow-on edits needed to keep the plan coherent.
|
||||
4. **Confirm and apply** — show each proposed revision, write only after the user confirms.
|
||||
5. **Point to the next step** — report where the change now stands and recommend what comes next: artifacts still missing → `/opsx:continue`; plan revised after the change was already implemented → `/opsx:apply` to carry the delta into code; everything done and implemented → `/opsx:archive`. Guidance only — the skill never acts on it.
|
||||
|
||||
Two guardrails make it the command the cluster asked for:
|
||||
|
||||
- **Planning artifacts only, never code.** If a revised plan implies code changes, it hands off to `/opsx:apply` ([#1188](https://github.com/Fission-AI/OpenSpec/issues/1188)).
|
||||
- **Schema-driven, not name-driven.** Artifact ids and paths come from `openspec status`, so the skill works for custom schemas, not just the default `proposal → specs → design → tasks` ([#777](https://github.com/Fission-AI/OpenSpec/issues/777), [#666](https://github.com/Fission-AI/OpenSpec/issues/666)).
|
||||
|
||||
**Coherence is bidirectional.** Earlier framing treated update as strictly "downstream" propagation. That is wrong: in `proposal → specs → design → tasks`, editing `design` can require revising `proposal` too. The skill reads the change's artifacts and reconciles them in whatever direction the edit demands, rather than assuming a fixed flow.
|
||||
|
||||
### Deliberately not built (yet)
|
||||
|
||||
Per the steer to introduce as little code as possible, and only when there is a defined need, this change does **not** add: a reverse-dependency graph API, content digests / staleness signals, a `.openspec.yaml` baseline ledger, an `openspec reconcile` write op, a drift report, or a `status --impact` selector. The agent reads the change's artifacts directly — a handful of markdown files — which is enough to judge coherence. If a future, concrete need emerges (e.g. unattended drift detection across many changes), exposing the schema's `requires` edges on `openspec status --json` is a one-field additive follow-up. It is out of scope here.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `opsx-update-skill`: A new `/opsx:update` workflow skill that revises a change's existing planning artifacts and keeps them coherent with one another. It reads the artifact set and paths from `openspec status`, reviews related artifacts in any direction (not only downstream), edits planning artifacts only and never code, and confirms each edit with the user. It ends with next-step guidance — recommending `/opsx:continue`, `/opsx:apply`, or `/opsx:archive` based on the change's state — without acting on it.
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/templates/workflows/update-change.ts` (**new**) — the `openspec-update-change` skill template and the `/opsx:update` command template, mirroring the structure of `continue-change.ts`. Reads artifact ids and paths from `openspec status --json`; embeds no artifact-name patterns.
|
||||
- Skill/command registration + [src/core/profiles.ts](../../../src/core/profiles.ts) — add `update` to `ALL_WORKFLOWS` **and to the default `core` profile** (`propose`, `explore`, `apply`, `sync`, `archive`), so `/opsx:update` is part of the default install rather than expanded-only (maintainer call on the PR).
|
||||
- `docs/opsx.md` — add a `/opsx:update` row to the command table and a short "Updating a change" usage note.
|
||||
- `openspec/changes/add-artifact-regeneration-support/` — the in-repo proposal-only stub for this gap is superseded; retire it or fold its notes into design.
|
||||
- No changes to `src/core/artifact-graph/*`, `src/commands/workflow/status.ts`, or `ChangeMetadataSchema`. The skill uses `openspec status` / `openspec list` as they exist today.
|
||||
|
||||
## Issues addressed
|
||||
|
||||
Verified against `Fission-AI/OpenSpec` on 2026-06-30.
|
||||
|
||||
Closes (the missing-update-action family):
|
||||
|
||||
- [#1188](https://github.com/Fission-AI/OpenSpec/issues/1188) — "Add a command to update proposal, design and task" (and stop it editing code). Delivered as `/opsx:update`, planning-artifacts-only.
|
||||
- [#705](https://github.com/Fission-AI/OpenSpec/issues/705) — "Rebuild downstream artifacts from a modified upstream." Delivered as the skill's read-and-reconcile pass over the change's artifacts.
|
||||
- [#673](https://github.com/Fission-AI/OpenSpec/issues/673) — "clarify": update existing artifacts without auto-advancing the build frontier. `/opsx:update` revises in place and never creates the next artifact.
|
||||
- [#247](https://github.com/Fission-AI/OpenSpec/issues/247) — "review and update all change proposals." Delivered as the within-a-change coherence review; cross-change audit is a separate, later proposal.
|
||||
|
||||
Answers (questions whose honest answer today is "no command exists"):
|
||||
|
||||
- [#694](https://github.com/Fission-AI/OpenSpec/issues/694), [#684](https://github.com/Fission-AI/OpenSpec/issues/684), [#618](https://github.com/Fission-AI/OpenSpec/issues/618) — "which command regenerates a document after the flow progressed / after apply?" → `/opsx:update`.
|
||||
- Discussion [#1206](https://github.com/Fission-AI/OpenSpec/discussions/1206) — the official answer becomes `/opsx:update`.
|
||||
|
||||
Supersedes:
|
||||
|
||||
- `openspec/changes/add-artifact-regeneration-support` (in-repo, proposal-only stub) — same problem, replaced by this skill. Its hardcoded-filename dependency tracking and metadata-file staleness mechanism are dropped in favor of letting the agent read the artifacts.
|
||||
|
||||
Delineated from adjacent commands (distinct surfaces — coordinate, don't collide):
|
||||
|
||||
- [#702](https://github.com/Fission-AI/OpenSpec/pull/702) `/opsx:clarify` — resolves ambiguity *within one artifact* via Q&A; a complementary upstream step. `/opsx:update` then reconciles the change's artifacts with each other.
|
||||
- [#1251](https://github.com/Fission-AI/OpenSpec/pull/1251) `/opsx:review`, [#880](https://github.com/Fission-AI/OpenSpec/issues/880) — review the *implementation (code)* against the plan. `/opsx:update` is the mirror image: it keeps the *plan* coherent and never touches code.
|
||||
- [#783](https://github.com/Fission-AI/OpenSpec/issues/783) — cross-artifact quality review. The skill's coherence pass is the lightweight form of this; a deterministic `validate`-side check is a separate proposal.
|
||||
@@ -0,0 +1,138 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Update Workflow Command
|
||||
|
||||
The system SHALL provide a `/opsx:update` workflow skill that revises a change's existing planning artifacts in place. It SHALL NOT advance the build frontier (it does not create a not-yet-started artifact) and SHALL edit planning artifacts only, never implementation code.
|
||||
|
||||
#### Scenario: Select the change to update
|
||||
|
||||
- **WHEN** the user invokes `/opsx:update` without a change name
|
||||
- **THEN** the skill infers the change from conversation context if possible
|
||||
- **AND** if it cannot, it lists available changes (most-recently-modified first) via `openspec list --json` and asks the user to choose, never auto-selecting
|
||||
|
||||
#### Scenario: Revise without advancing the frontier
|
||||
|
||||
- **WHEN** the user asks `/opsx:update` to revise an existing artifact
|
||||
- **THEN** the skill updates that artifact and reconciles the change's other existing artifacts with it
|
||||
- **AND** it does NOT create any artifact that does not yet exist (that remains the job of `/opsx:continue`/`/opsx:propose`)
|
||||
|
||||
#### Scenario: Missing artifacts are deferred to continue
|
||||
|
||||
- **WHEN** keeping the change coherent would require an artifact that has not been created yet
|
||||
- **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
|
||||
|
||||
#### Scenario: Update stays within the plan
|
||||
|
||||
- **WHEN** revising artifacts would imply changes to implementation code
|
||||
- **THEN** the skill updates the planning artifacts only
|
||||
- **AND** it directs the user to `/opsx:apply` to carry the revised plan into code, rather than editing code itself
|
||||
|
||||
### Requirement: Schema-Driven Artifact Resolution
|
||||
|
||||
The `/opsx:update` skill SHALL learn which artifacts exist and where they live by reading the change's status from the CLI, and SHALL NOT rely on hardcoded artifact names or assumed path separators. This makes the skill correct for custom schemas and on every platform, not only the default `spec-driven` schema.
|
||||
|
||||
#### Scenario: Reads the artifact set from status
|
||||
|
||||
- **WHEN** the skill needs to know which artifacts a change has and where they are
|
||||
- **THEN** it runs `openspec status --change <id> --json` and uses the reported artifact ids, statuses, and the `artifactPaths` map (`existingOutputPaths` for the files to edit)
|
||||
- **AND** it does not assume the artifact ids or output paths
|
||||
|
||||
#### Scenario: Does not branch on hardcoded artifact names
|
||||
|
||||
- **WHEN** the skill decides which artifacts to read and revise
|
||||
- **THEN** its control flow uses the ids reported by the CLI
|
||||
- **AND** it does not branch on literal `proposal`/`specs`/`design`/`tasks` names
|
||||
|
||||
#### Scenario: Works for a custom schema
|
||||
|
||||
- **WHEN** the active change uses a custom schema whose artifact ids are not `proposal`/`specs`/`design`/`tasks`
|
||||
- **THEN** the skill uses the artifact ids and paths reported by the CLI
|
||||
- **AND** it works without any change to the skill
|
||||
|
||||
#### Scenario: Resolve artifact paths cross-platform
|
||||
|
||||
- **WHEN** the skill reads or writes an artifact on macOS, Linux, or Windows
|
||||
- **THEN** it uses the `existingOutputPaths` provided by the CLI status output
|
||||
- **AND** it does not assume forward-slash separators
|
||||
|
||||
#### Scenario: Edit the concrete files of a glob artifact
|
||||
|
||||
- **WHEN** an artifact's declared output path is a glob (for example `specs/**/*.md`)
|
||||
- **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
|
||||
|
||||
- **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
|
||||
|
||||
### Requirement: Bidirectional Coherence Review
|
||||
|
||||
The `/opsx:update` skill SHALL keep a change's existing planning artifacts coherent with one another after a revision, reviewing affected artifacts in any direction rather than assuming a fixed downstream flow.
|
||||
|
||||
#### Scenario: Reconcile related artifacts after an edit
|
||||
|
||||
- **WHEN** the user revises one artifact
|
||||
- **THEN** the skill reviews the change's other existing artifacts against the revision
|
||||
- **AND** it proposes follow-on edits to any artifact that is now inconsistent, whether that artifact is upstream or downstream of the edited one
|
||||
|
||||
#### Scenario: Upstream artifact may be revised
|
||||
|
||||
- **WHEN** an edit to a later artifact (for example design) contradicts an earlier one (for example the proposal)
|
||||
- **THEN** the skill may propose revising the earlier artifact to restore coherence
|
||||
- **AND** it does not treat propagation as downstream-only
|
||||
|
||||
#### Scenario: Coherence review with no specific edit
|
||||
|
||||
- **WHEN** the user invokes `/opsx:update` without a specific revision in mind ("make this change coherent")
|
||||
- **THEN** the skill reads the change's existing artifacts and reviews them against each other for contradictions, gaps, and duplication
|
||||
- **AND** it presents any findings for the user to confirm before editing
|
||||
|
||||
#### Scenario: Coherent change yields no changes
|
||||
|
||||
- **WHEN** the skill finds the change's artifacts already coherent
|
||||
- **THEN** it reports the change as coherent and makes no edits
|
||||
|
||||
### Requirement: Next-Step Guidance
|
||||
|
||||
After applying confirmed revisions (or finding none needed), the `/opsx:update` skill SHALL report where the change stands and recommend the next command, without acting on the recommendation itself.
|
||||
|
||||
#### Scenario: Updating an already-implemented change
|
||||
|
||||
- **WHEN** the user updates a change whose implementation already happened (for example tasks are checked off or `/opsx:apply` was already run)
|
||||
- **THEN** the skill still revises planning artifacts only
|
||||
- **AND** it notes that the implementation may no longer match the revised plan and recommends `/opsx:apply` to carry the delta into code
|
||||
- **AND** it does not implement anything itself
|
||||
|
||||
#### Scenario: Next step when artifacts are incomplete
|
||||
|
||||
- **WHEN** the update finishes and the change still has not-yet-created artifacts
|
||||
- **THEN** the skill recommends `/opsx:continue` to create them
|
||||
|
||||
#### Scenario: Next step when the change is fully done
|
||||
|
||||
- **WHEN** the update finishes and the change's artifacts are complete and already implemented
|
||||
- **THEN** the skill recommends `/opsx:archive`
|
||||
|
||||
### Requirement: User-Confirmed Incremental Application
|
||||
|
||||
The `/opsx:update` skill SHALL propose each artifact revision and apply it only after user confirmation.
|
||||
|
||||
#### Scenario: Confirm before writing
|
||||
|
||||
- **WHEN** the skill has a proposed revision for an artifact
|
||||
- **THEN** it shows the user what it intends to change and why before writing
|
||||
- **AND** it writes only after the user confirms
|
||||
|
||||
#### Scenario: Rejected revision is not written
|
||||
|
||||
- **WHEN** the user rejects a proposed revision for an artifact
|
||||
- **THEN** the skill does not write that revision
|
||||
- **AND** the artifact is left unchanged
|
||||
|
||||
#### Scenario: Intent change is redirected to a new change
|
||||
|
||||
- **WHEN** the requested revision changes the intent of the change rather than refining it (per the "Update vs. Start Fresh" heuristic)
|
||||
- **THEN** the skill recommends starting a new change (`/opsx:new`) instead of mutating the existing proposal into different work
|
||||
@@ -0,0 +1,30 @@
|
||||
# Tasks: `/opsx:update` — a thin update skill
|
||||
|
||||
> The whole feature is one new skill template over the existing `openspec status` / `openspec list` commands. No changes to the graph engine, the `status` command, or the metadata schema.
|
||||
|
||||
## 1. The `/opsx:update` skill
|
||||
|
||||
- [x] 1.1 Create `src/core/templates/workflows/update-change.ts` with `getUpdateChangeSkillTemplate()` (skill) and `getOpsxUpdateCommandTemplate()` (command), mirroring `continue-change.ts`. The skill name is `openspec-update-change` — change-scoped, per the `openspec-<verb>-change` convention (see design, Naming).
|
||||
- [x] 1.2 Instruction body (see design "The skill, written by hand"): resolve the change (infer / `openspec list --json` / ask) → `openspec status --change <id> --json` → read the relevant artifacts → apply the requested edit → reconcile the change's other existing artifacts in any direction → confirm and apply one artifact at a time → end with next-step guidance (`/opsx:continue` / `/opsx:apply` / `/opsx:archive` based on the change's state; see design Decision 6), never acting on it. Read artifact ids from the status JSON only, and write to `artifactPaths.<id>.existingOutputPaths` (never to a glob `resolvedOutputPath`).
|
||||
- [x] 1.3 Encode the guardrails: (a) planning artifacts only — never edit code, hand off to `/opsx:apply`; (b) schema-driven — no branching on literal `proposal`/`specs`/`design`/`tasks`; ids/paths come from `openspec status`; (c) revise only existing files (`existingOutputPaths`) — defer not-yet-created artifacts, and new files under a glob artifact, to `/opsx:continue`; (d) intent change → recommend `/opsx:new` (the "Update vs. Start Fresh" heuristic in `docs/opsx.md`).
|
||||
- [x] 1.4 Register the skill/command and add `update` to `ALL_WORKFLOWS` **and the default `core` profile** in `src/core/profiles.ts` (maintainer call: default install, not expanded-only).
|
||||
|
||||
## 2. Docs & supersede the stub
|
||||
|
||||
- [x] 2.1 Add a `/opsx:update` row to the command table in `docs/opsx.md`, plus a short "Updating a change" usage note.
|
||||
- [x] 2.2 Remove (or fold) `openspec/changes/add-artifact-regeneration-support/` so the tree has a single update proposal.
|
||||
- [x] 2.3 Update any generated-skill manifests/fixtures that enumerate workflow skills so `openspec-update-change` is included.
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [x] 3.1 Template generation snapshot for the skill and command templates.
|
||||
- [x] 3.2 Assert the template's control flow contains NO hardcoded artifact-name branching (the anti-#777 guard): artifact ids must be read from `openspec status` JSON.
|
||||
- [x] 3.3 Assert the template instructs planning-artifacts-only with a hand-off to `/opsx:apply` for code, and never advances the build frontier.
|
||||
- [x] 3.4 Assert the template instructs writing to `existingOutputPaths` (the glob-expanded concrete files) and not to a glob `resolvedOutputPath`.
|
||||
- [x] 3.5 Assert the template ends with next-step guidance (`/opsx:continue`/`/opsx:apply`/`/opsx:archive`) and instructs the agent never to act on it.
|
||||
- [x] 3.6 Assert `update` is included in the `core` profile's workflows (profiles test).
|
||||
|
||||
## 4. End-to-end verification
|
||||
|
||||
- [x] 4.1 `openspec validate add-update-workflow --strict` passes; `openspec status --change add-update-workflow` shows all artifacts complete.
|
||||
- [x] 4.2 Manual walk-through: on a `spec-driven` change, edit `design`, run `/opsx:update`, confirm it proposes coherence edits to other existing artifacts (including upstream where warranted) and never touches code.
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-04-23
|
||||
@@ -0,0 +1,3 @@
|
||||
# add-kimi-cli-skills-only-support
|
||||
|
||||
Add Kimi CLI as a supported skills-only tool without a command adapter
|
||||
@@ -0,0 +1,85 @@
|
||||
## Context
|
||||
|
||||
Kimi CLI is not another Claude/Codex-style adapter target. Its extension model is built around discovered skills, not external command files:
|
||||
|
||||
- skills are discovered from `.kimi/skills/`
|
||||
- skills are exposed as `/skill:<name>`
|
||||
- no stable `.kimi/commands/` or prompt-file loading mechanism was found in the Kimi CLI codebase
|
||||
|
||||
OpenSpec's existing architecture can already represent that shape:
|
||||
|
||||
- `AI_TOOLS` can advertise a `skillsDir`
|
||||
- `init` can install skills for any selected tool with `skillsDir`
|
||||
- when command generation is attempted for a tool without an adapter, OpenSpec already records `commandsSkipped`
|
||||
|
||||
## Goals
|
||||
|
||||
- Add Kimi CLI using the same narrow `skills-only` pattern already used by Trae
|
||||
- Keep the implementation small: metadata, docs, and a focused regression test
|
||||
- Make the spec text match the current code path for adapterless tools
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- designing a Kimi-specific command adapter without upstream support
|
||||
- changing tool capability modeling across the whole generation pipeline
|
||||
- reworking `delivery=commands` behavior for all adapterless tools
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Represent Kimi CLI as an adapterless tool with `.kimi`
|
||||
|
||||
Add a new `AI_TOOLS` entry:
|
||||
|
||||
```ts
|
||||
{ name: 'Kimi CLI', value: 'kimi', available: true, successLabel: 'Kimi CLI', skillsDir: '.kimi' }
|
||||
```
|
||||
|
||||
This matches Kimi CLI's project-local skills root and lets existing init/update detection paths treat it as a supported tool.
|
||||
|
||||
### 2. Do not add a Kimi command adapter
|
||||
|
||||
No `src/core/command-generation/adapters/kimi.ts` file will be added, and the command adapter registry will remain unchanged.
|
||||
|
||||
Rationale:
|
||||
|
||||
- Kimi CLI exposes skills dynamically as `/skill:<name>`
|
||||
- the previous upstream PR stalled specifically because no legitimate adapter target was available
|
||||
- adding a fake `.kimi/commands/...` path would create behavior OpenSpec cannot justify against upstream Kimi CLI behavior
|
||||
|
||||
### 3. Document Kimi by its real invocation surface
|
||||
|
||||
Kimi documentation in OpenSpec must use Kimi's actual skill invocation form:
|
||||
|
||||
- supported-tools: no generated command files, use `/skill:openspec-*`
|
||||
- commands doc: examples such as `/skill:openspec-propose`
|
||||
|
||||
The docs must not claim generated `opsx-*` files or `/openspec-*` direct invocations for Kimi.
|
||||
|
||||
### 4. Keep the change compatible with existing Trae-style behavior
|
||||
|
||||
This change intentionally follows the current adapterless-tool behavior already present in the codebase:
|
||||
|
||||
- skills are created whenever delivery includes skills
|
||||
- command generation is skipped when no adapter exists
|
||||
- init output reports `Commands skipped for: kimi (no adapter)`
|
||||
|
||||
This keeps the Kimi change small and avoids overlapping implementation work already captured in `add-tool-command-surface-capabilities`.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
Add one focused regression test in `test/core/init.test.ts`:
|
||||
|
||||
- configure `delivery=both`
|
||||
- run init with `--tools kimi`
|
||||
- verify Kimi skills are created under `.kimi/skills/...`
|
||||
- verify init reports the skipped command generation path for `kimi`
|
||||
|
||||
That test is enough for this narrow change 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 being introduced
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
The main trade-off is scope: Kimi will inherit the current adapterless-tool behavior, including the broader limitation that `delivery=commands` is not yet capability-aware for skills-invocable tools. That is acceptable for this change because it matches the existing Trae/ForgeCode model and keeps the implementation aligned with verified Kimi CLI behavior.
|
||||
@@ -0,0 +1,38 @@
|
||||
## Why
|
||||
|
||||
OpenSpec already has user demand for Kimi CLI support, but the previous upstream attempt stalled because it assumed Kimi needed a command adapter. Local review of the Kimi CLI codebase shows a different integration surface: Kimi discovers `SKILL.md` files from `.kimi/skills/` and exposes them through `/skill:<name>`, but it does not provide a stable, file-based custom command directory like Claude Code or Codex.
|
||||
|
||||
OpenSpec already supports tools that install skills without a command adapter. Trae and ForgeCode are the existing examples. Kimi should follow the same pattern instead of introducing undocumented `.kimi/commands/...` behavior.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add Kimi CLI as a supported tool in `AI_TOOLS` with `skillsDir: '.kimi'`
|
||||
- Document Kimi CLI as a skills-only integration in supported tools and command usage docs
|
||||
- Align change specs so `cli-init` explicitly allows selected tools with `skillsDir` but no registered command adapter
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_None._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ai-tool-paths`: define the `.kimi` skills root for Kimi CLI
|
||||
- `cli-init`: clarify that adapterless tools remain valid selections and skip command-file generation with an informational message
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/config.ts` - add Kimi CLI tool metadata
|
||||
- `docs/supported-tools.md` - add Kimi CLI row and tool id
|
||||
- `docs/commands.md` - document `/skill:openspec-*` usage for Kimi CLI
|
||||
- `docs/cli.md` - include `kimi` in the supported `--tools` list
|
||||
- `test/core/init.test.ts` - cover Kimi CLI as an adapterless tool during init
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Adding `src/core/command-generation/adapters/kimi.ts`
|
||||
- Defining a `.kimi/commands/...` output path
|
||||
- Changing the broader delivery model for adapterless tools under `delivery=commands`
|
||||
|
||||
That broader capability-aware delivery work is already being explored separately in `add-tool-command-surface-capabilities`. This change stays narrow and follows the existing Trae/ForgeCode pattern.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# 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: Kimi CLI paths defined
|
||||
|
||||
- **WHEN** looking up the `kimi` tool
|
||||
- **THEN** `skillsDir` SHALL be `.kimi`
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# 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
|
||||
@@ -0,0 +1,22 @@
|
||||
## 1. Change Artifacts
|
||||
|
||||
- [x] 1.1 Write proposal, design, and spec deltas for Kimi CLI skills-only support
|
||||
|
||||
## 2. Tool Metadata
|
||||
|
||||
- [x] 2.1 Add `Kimi CLI` to `src/core/config.ts` with `value: 'kimi'` and `skillsDir: '.kimi'`
|
||||
|
||||
## 3. Documentation
|
||||
|
||||
- [x] 3.1 Update `docs/supported-tools.md` with a Kimi CLI row that clearly states there is no command adapter
|
||||
- [x] 3.2 Update `docs/commands.md` to document Kimi CLI usage via `/skill:openspec-*`
|
||||
- [x] 3.3 Update `docs/cli.md` so the supported `--tools` list includes `kimi`
|
||||
|
||||
## 4. Tests
|
||||
|
||||
- [x] 4.1 Add a targeted init regression test for `--tools kimi` under adapterless command generation
|
||||
|
||||
## 5. Validation
|
||||
|
||||
- [x] 5.1 Validate the change artifacts with `openspec validate`
|
||||
- [x] 5.2 Run targeted tests and fix any regressions
|
||||
@@ -0,0 +1,208 @@
|
||||
## Product Model
|
||||
|
||||
An OpenSpec workspace is the durable planning home for work that spans multiple repos or folders.
|
||||
|
||||
It should feel like this:
|
||||
|
||||
```text
|
||||
workspace = where related changes live
|
||||
link = a named repo or folder the workspace can plan against
|
||||
change = one feature, fix, project, or other planned piece of work
|
||||
```
|
||||
|
||||
The foundation intentionally avoids the rest of the workflow. It only defines how OpenSpec recognizes a workspace, where managed workspaces live, how linked paths are represented, and how shared state differs from local state.
|
||||
|
||||
A workspace is not a feature. It can hold many changes over time. The linked repos or folders provide planning context, while the code stays where it is.
|
||||
|
||||
## Workspace Shape
|
||||
|
||||
OpenSpec workspaces use this shape:
|
||||
|
||||
```text
|
||||
workspace-root/
|
||||
changes/ # workspace-level proposals, tasks, specs
|
||||
.openspec-workspace/
|
||||
workspace.yaml # shared workspace information
|
||||
local.yaml # this machine's paths and preferences
|
||||
```
|
||||
|
||||
The user-facing planning surface is `changes/`. The identity file that makes the directory a workspace is `.openspec-workspace/workspace.yaml`.
|
||||
|
||||
Repo-local projects keep the existing shape:
|
||||
|
||||
```text
|
||||
repo-root/
|
||||
openspec/
|
||||
specs/
|
||||
changes/
|
||||
```
|
||||
|
||||
That distinction lets a user or agent tell which surface they are working in:
|
||||
|
||||
```text
|
||||
coordination workspace -> shared cross-repo planning
|
||||
repo-local project -> repo-owned specs and implementation planning
|
||||
```
|
||||
|
||||
Users should not run repo-local `openspec init` inside the workspace root. A workspace is already an OpenSpec coordination surface; it is not a product repo adopting repo-local OpenSpec.
|
||||
|
||||
## Workspace Names
|
||||
|
||||
A workspace name is a simple folder-style identifier, not a display name.
|
||||
|
||||
The name must be usable as a folder name in the current runtime. It must not be empty, must not be `.` or `..`, and must not contain path separators.
|
||||
|
||||
OpenSpec should not maintain a cross-platform reserved-name list in this slice. Setup/create flows should let filesystem creation surface OS-specific invalid folder names, then report that failure clearly.
|
||||
|
||||
The same workspace name is stored in `.openspec-workspace/workspace.yaml`, used as the default managed workspace folder name, and used as the local registry name.
|
||||
|
||||
## Shared And Local State
|
||||
|
||||
Workspace state follows a simple sharing rule:
|
||||
|
||||
```text
|
||||
share stable link names and planning
|
||||
keep local checkout paths local
|
||||
```
|
||||
|
||||
Expected shared state:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
name: platform
|
||||
links:
|
||||
api: {}
|
||||
web: {}
|
||||
```
|
||||
|
||||
Expected local state:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
paths:
|
||||
api: /repos/api
|
||||
web: /repos/web
|
||||
```
|
||||
|
||||
Later slices can expand these shapes, but the product rule should stay stable: a shared workspace should not commit one user's absolute checkout paths.
|
||||
|
||||
OpenSpec-created workspaces should include an ignore rule for `.openspec-workspace/local.yaml` so local checkout paths are not accidentally shared. `.openspec-workspace/workspace.yaml` remains the portable workspace identity and link-name state.
|
||||
|
||||
## Workspace Location
|
||||
|
||||
OpenSpec should create managed workspaces in one standard place:
|
||||
|
||||
```text
|
||||
getGlobalDataDir()/workspaces
|
||||
```
|
||||
|
||||
That reuses existing OpenSpec data-directory behavior:
|
||||
|
||||
- `$XDG_DATA_HOME/openspec/workspaces` when `XDG_DATA_HOME` is set
|
||||
- `~/.local/share/openspec/workspaces` on Unix/macOS fallback
|
||||
- `%LOCALAPPDATA%\openspec\workspaces` on native Windows fallback
|
||||
|
||||
This slice intentionally does not define a workspace-specific environment-variable, command, or configuration override for managed workspace storage. Tests should rely on existing global data-directory controls and test helpers instead of a separate workspace-home override.
|
||||
|
||||
This is deliberately quiet. The product should not ask most users where workspaces should live.
|
||||
|
||||
OpenSpec should show the resolved workspace path after setup. Quiet defaults should avoid a prompt, not hide where planning files were created.
|
||||
|
||||
## Local Workspace Registry
|
||||
|
||||
OpenSpec should keep a lightweight local registry of known workspaces:
|
||||
|
||||
```text
|
||||
getGlobalDataDir()/workspaces/registry.yaml
|
||||
```
|
||||
|
||||
Expected registry state:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
workspaces:
|
||||
platform: /Users/tabish/.local/share/openspec/workspaces/platform
|
||||
checkout: /Users/tabish/.local/share/openspec/workspaces/checkout
|
||||
```
|
||||
|
||||
The registry is a local index, not the source of truth. It exists so workspace commands can work from anywhere, show a picker when multiple workspaces exist, and list known workspaces without scanning arbitrary folders.
|
||||
|
||||
Each workspace folder remains authoritative for its own `.openspec-workspace/workspace.yaml` and `.openspec-workspace/local.yaml`. If a registry entry points at a missing or invalid workspace, later check/list flows can report that and suggest a repair.
|
||||
|
||||
## Windows And WSL2
|
||||
|
||||
Path behavior is runtime-local:
|
||||
|
||||
- PowerShell/native Windows uses Windows paths and Windows data-directory fallback.
|
||||
- WSL2 uses Linux paths and Linux/XDG fallback inside WSL.
|
||||
- Local repo paths are stored as the user supplied them for the current runtime.
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
PowerShell:
|
||||
default base -> %LOCALAPPDATA%\openspec\workspaces
|
||||
|
||||
WSL2:
|
||||
default base -> ~/.local/share/openspec/workspaces
|
||||
```
|
||||
|
||||
This slice should not translate between `D:\repo`, `/mnt/d/repo`, and `\\wsl$` paths. Cross-runtime translation can be reconsidered later if an agent-launch workflow requires it.
|
||||
|
||||
## Link Names
|
||||
|
||||
A link name is the stable way to refer to a repo or folder inside workspace planning.
|
||||
|
||||
The local path can vary by machine:
|
||||
|
||||
```text
|
||||
shared link name: landing
|
||||
Tabish path: /Users/tabish/repos/landing
|
||||
Windows path: D:\repos\landing
|
||||
WSL2 path: /mnt/d/repos/landing
|
||||
```
|
||||
|
||||
Later workflows should refer to `landing` in workspace planning, status, and apply context. The local path is only how the current machine finds that repo or folder.
|
||||
|
||||
Link names are intentionally minimal: they must be non-empty, must not be `.` or `..`, must not contain path separators, and must be unique within the workspace.
|
||||
|
||||
The owning repo or folder remains the home of canonical specs and implementation work. The workspace makes the cross-boundary plan legible; it does not take ownership away from the linked repos or folders.
|
||||
|
||||
Link names are normally inferred from the folder basename in guided flows. Direct flows can allow an explicit name when the default would conflict or be unclear.
|
||||
|
||||
## Linked Repos And Folders
|
||||
|
||||
Workspace planning visibility should not require repo-local OpenSpec state.
|
||||
|
||||
That matters for two common cases:
|
||||
|
||||
- a repo has not adopted OpenSpec yet, but still needs to be considered in planning
|
||||
- a large monorepo has folders such as packages, services, or apps that should be planned like separate areas, without each folder having its own `openspec/`
|
||||
|
||||
Foundation should allow the link model to describe both:
|
||||
|
||||
```text
|
||||
multi-repo:
|
||||
api -> /repos/api
|
||||
web -> /repos/web
|
||||
|
||||
large monorepo:
|
||||
billing -> /repos/platform/services/billing
|
||||
checkout -> /repos/platform/apps/checkout
|
||||
```
|
||||
|
||||
Later apply/verify/archive workflows can decide what extra readiness is needed for implementation. Planning should be able to start before that.
|
||||
|
||||
Linking only records the relationship between a workspace link name and a local path. It must not create, copy, move, initialize, or edit files inside the linked repo or folder.
|
||||
|
||||
Repo-local spec availability is computed when needed. For example, `repo_specs_path` can be reported by a later doctor command when a linked path contains `openspec/specs`, but that path should not be treated as required workspace state.
|
||||
|
||||
## Later Slices
|
||||
|
||||
This foundation stops before user-facing workspace workflows:
|
||||
|
||||
- `workspace-create-and-register-repos` owns setup, link, relink, list, and doctor behavior.
|
||||
- `workspace-open-agent-context` owns agent launch context.
|
||||
- `workspace-change-planning` owns workspace proposals and repo scope.
|
||||
- `workspace-apply-repo-slice` owns implementation of one repo slice.
|
||||
- `workspace-verify-and-archive` owns completion and archive behavior.
|
||||
@@ -0,0 +1,142 @@
|
||||
## Why
|
||||
|
||||
Users need a workspace to feel like the obvious home for planning across multiple repos or folders.
|
||||
|
||||
They should be able to think:
|
||||
|
||||
```text
|
||||
I have repos or folders that are often planned together.
|
||||
I create an OpenSpec workspace.
|
||||
That workspace is where changes live.
|
||||
My code stays where it is.
|
||||
OpenSpec links the workspace to those local paths.
|
||||
```
|
||||
|
||||
A workspace is not a feature. It is the durable planning home. Individual features, fixes, and projects are changes inside the workspace.
|
||||
|
||||
Users should not have to choose a storage location, create a change early, or understand internal workspace state before OpenSpec can orient itself.
|
||||
|
||||
The POC proved that workspace state is useful. This reimplementation should turn that into a simple product model that users and agents can explain without special-case vocabulary.
|
||||
|
||||
## What Changes
|
||||
|
||||
This change defines the user-facing foundation for OpenSpec workspaces.
|
||||
|
||||
An OpenSpec workspace has a recognizable planning home:
|
||||
|
||||
```text
|
||||
workspace-root/
|
||||
changes/
|
||||
.openspec-workspace/
|
||||
```
|
||||
|
||||
`changes/` is where workspace-level planning lives. `.openspec-workspace/` identifies the directory as an OpenSpec workspace and stores workspace state.
|
||||
|
||||
OpenSpec-managed workspaces live in one standard location:
|
||||
|
||||
```text
|
||||
<global-data-dir>/workspaces/
|
||||
```
|
||||
|
||||
Users should not need to choose that location. OpenSpec still shows the workspace path after setup so users know where planning files live. This foundation slice does not provide a workspace-specific environment-variable or configuration override for managed workspace storage.
|
||||
|
||||
OpenSpec also keeps a lightweight local registry of known workspaces on the current machine. The registry powers global commands, pickers, and listing, but each workspace folder remains the source of truth.
|
||||
|
||||
Workspace state is split by user expectation:
|
||||
|
||||
- shared workspace information can move between machines
|
||||
- local checkout paths stay local to each machine
|
||||
- linked repos and folders are referred to by stable link names, not by absolute paths
|
||||
|
||||
A linked path can be a full repo, a folder inside a monorepo, or another existing folder the workspace should plan against. A linked path does not need repo-local `openspec/` state before it can be included in workspace planning. Repo-local OpenSpec state may still matter later for implementation, verification, or archive workflows, but it is not a prerequisite for planning visibility.
|
||||
|
||||
Native Windows/PowerShell and WSL2 are both supported. Each runtime uses its own path conventions. OpenSpec does not translate paths between Windows and WSL in this foundation slice.
|
||||
|
||||
## Outcome
|
||||
|
||||
After this change, later workspace features can rely on one clear product contract:
|
||||
|
||||
- OpenSpec can tell when the user is inside a workspace.
|
||||
- OpenSpec knows where to create managed workspaces by default.
|
||||
- OpenSpec can keep a local registry of known workspaces.
|
||||
- A workspace has one visible planning area: `changes/`.
|
||||
- Workspace state is distinguishable from repo-local `openspec/` state.
|
||||
- Shared workspace state does not force one user's local paths onto another user.
|
||||
- Workspace planning can reference existing repos or folders by stable link names.
|
||||
- Linked repos or folders do not need repo-local OpenSpec state for workspace planning.
|
||||
- Multi-repo and large-monorepo work can use the same workspace planning model.
|
||||
- Repo-owned specs and implementation remain owned by their repos or source areas.
|
||||
- Windows, PowerShell, and WSL2 path behavior is predictable.
|
||||
|
||||
This change does not deliver the full workspace workflow. It gives `workspace-create-and-register-repos` the foundation it needs to add the first user-facing commands.
|
||||
|
||||
## POC Findings
|
||||
|
||||
Behavior to preserve:
|
||||
|
||||
- A workspace is a durable coordination home for cross-repo planning.
|
||||
- The workspace has a visible `changes/` directory at its root.
|
||||
- Linked repos and folders provide the context the workspace can plan against.
|
||||
- Stable link names matter more than local checkout paths.
|
||||
- Local machine paths should not become shared workspace state.
|
||||
- Canonical specs and implementation still belong to the owning repos.
|
||||
|
||||
Lessons to carry forward:
|
||||
|
||||
- The POC's hidden `.openspec/` workspace metadata shape made workspace state too easy to confuse with repo-local OpenSpec state.
|
||||
- Users should not need to run repo-local `openspec init` inside the workspace root.
|
||||
- The POC's requirement that registered repos already have `openspec/` is too strict for planning. Repos and folders should be linkable before they adopt repo-local OpenSpec state.
|
||||
- Repo or folder visibility should not depend on creating a change.
|
||||
- Workspace setup should not imply repo-local implementation, branch, worktree, apply, verify, or archive behavior.
|
||||
- `add-repo` is too narrow for the user-facing model. Linking an existing repo or folder is clearer.
|
||||
|
||||
## Decisions
|
||||
|
||||
- Workspace identity directory: `.openspec-workspace/`.
|
||||
- Workspace identity file: `.openspec-workspace/workspace.yaml`.
|
||||
- Workspace name: a valid folder name for the current OS, excluding empty names, `.`/`..`, and path separators.
|
||||
- Workspace name usage: stored in `workspace.yaml`, used as the default managed workspace folder name, and used as the local registry name.
|
||||
- Planning surface: top-level `changes/`.
|
||||
- Local machine state: `.openspec-workspace/local.yaml`.
|
||||
- Local machine state exclusion: OpenSpec-created workspaces exclude `.openspec-workspace/local.yaml` from portable collaboration state by default.
|
||||
- Local workspace registry: `<global-data-dir>/workspaces/registry.yaml`.
|
||||
- Default workspace base: `<global-data-dir>/workspaces/`.
|
||||
- Platform behavior: native Windows and WSL2 each use the path conventions of the runtime running OpenSpec.
|
||||
- Linked paths may be full repos, monorepo folders, or other existing folders.
|
||||
- Link names: non-empty stable names, unique within a workspace, excluding `.`/`..` and path separators.
|
||||
- Repo-local `openspec/` state is not required for workspace planning visibility.
|
||||
- Linking records the relationship only; it does not create, copy, move, initialize, or edit files in the linked repo or folder.
|
||||
|
||||
Planning dependency:
|
||||
|
||||
- None. This is the first implementation slice.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- No complete `openspec workspace setup`, `openspec workspace link`, or `openspec workspace relink` flow yet.
|
||||
- No public `openspec workspace create` command in the first user-facing workspace flow.
|
||||
- No user-facing command, environment variable, or configuration setting for changing the standard workspace location.
|
||||
- No question that asks users where OpenSpec should store workspaces by default.
|
||||
- No automatic Windows-to-WSL or WSL-to-Windows path translation.
|
||||
- No workspace-open agent launch behavior.
|
||||
- No workspace-level proposal creation.
|
||||
- No repo-slice apply, verify, archive, branch, or worktree behavior.
|
||||
- No copying workspace planning files into linked repos or folders as a side effect of creating, detecting, or linking a workspace.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `workspace-foundation`: Defines the product foundation for OpenSpec workspaces.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `openspec-conventions`: Describes how coordination workspaces differ from repo-local OpenSpec projects.
|
||||
|
||||
## Impact
|
||||
|
||||
- Workspace recognition and path behavior.
|
||||
- Workspace state parsing.
|
||||
- Local workspace registry parsing.
|
||||
- Documentation and agent guidance for the workspace mental model.
|
||||
- Later workspace slices should build on this contract instead of redefining workspace storage, identity, registry, or path behavior.
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace Product Language
|
||||
OpenSpec conventions SHALL describe coordination workspaces in user-facing product terms.
|
||||
|
||||
#### Scenario: Describing workspace structure
|
||||
- **WHEN** OpenSpec documentation describes workspace support
|
||||
- **THEN** it SHALL present a workspace as the planning home for work across linked repos or folders
|
||||
- **AND** it SHALL describe `changes/` as the workspace planning area
|
||||
|
||||
#### Scenario: Avoiding internal workspace vocabulary
|
||||
- **WHEN** OpenSpec documentation explains what a workspace includes
|
||||
- **THEN** it SHALL prefer plain product language such as "repos or folders"
|
||||
- **AND** it SHALL avoid user-facing reliance on terms such as "working set", "code area", "entry", "alias", or "local overlay"
|
||||
|
||||
#### Scenario: Distinguishing workspaces from changes
|
||||
- **WHEN** OpenSpec documentation explains workspace planning
|
||||
- **THEN** it SHALL describe a workspace as a durable planning home
|
||||
- **AND** it SHALL describe individual features, fixes, and projects as changes inside the workspace
|
||||
|
||||
#### Scenario: Distinguishing workspace and repo-local surfaces
|
||||
- **WHEN** OpenSpec documentation compares workspace and repo-local flows
|
||||
- **THEN** it SHALL explain that workspace planning lives in the workspace root
|
||||
- **AND** it SHALL explain that repo-local specs and changes continue to live under each repo's `openspec/` directory
|
||||
|
||||
#### Scenario: Sequencing the workspace roadmap
|
||||
- **WHEN** workspace reimplementation work is split across multiple active changes
|
||||
- **THEN** conventions SHALL allow those changes to remain flat siblings under `openspec/changes/`
|
||||
- **AND** dependency order MAY be documented in proposal prose until formal change stacking metadata is available
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Recognizable Workspace Home
|
||||
OpenSpec SHALL give users and agents a recognizable workspace home for cross-repo planning.
|
||||
|
||||
#### Scenario: Planning across linked repos or folders
|
||||
- **WHEN** a user creates an OpenSpec workspace for repos or folders they plan across
|
||||
- **THEN** the workspace SHALL provide a durable planning home
|
||||
- **AND** the workspace SHALL be able to hold multiple changes over time
|
||||
|
||||
#### Scenario: Working from inside a workspace
|
||||
- **GIVEN** a user runs OpenSpec from a workspace root or one of its subdirectories
|
||||
- **WHEN** OpenSpec resolves the current workspace
|
||||
- **THEN** it SHALL identify the workspace root
|
||||
- **AND** it SHALL use the workspace root's `changes/` directory as the workspace planning area
|
||||
|
||||
#### Scenario: Avoiding accidental workspace mode
|
||||
- **GIVEN** a directory has `changes/` but is not an OpenSpec workspace
|
||||
- **WHEN** OpenSpec resolves the current workspace
|
||||
- **THEN** it SHALL avoid treating that directory as a workspace
|
||||
- **AND** it SHALL enter workspace mode only when the workspace identity file is present
|
||||
|
||||
### Requirement: Stable Workspace Name
|
||||
OpenSpec SHALL use one folder-style workspace name across workspace identity, managed storage, and the local registry.
|
||||
|
||||
#### Scenario: Using one workspace name
|
||||
- **WHEN** OpenSpec creates or registers a managed workspace
|
||||
- **THEN** the workspace name SHALL be stored in `.openspec-workspace/workspace.yaml`
|
||||
- **AND** the same name SHALL be used as the default managed workspace folder name
|
||||
- **AND** the same name SHALL be used as the local registry name
|
||||
|
||||
#### Scenario: Rejecting invalid folder-style names
|
||||
- **WHEN** OpenSpec accepts a workspace name
|
||||
- **THEN** it SHALL reject empty names, `.` or `..`, and names containing path separators
|
||||
- **AND** setup or create flows SHALL report OS-level folder creation failures clearly
|
||||
|
||||
### Requirement: Dedicated Workspace Identity
|
||||
OpenSpec SHALL distinguish a coordination workspace from a repo-local OpenSpec project.
|
||||
|
||||
#### Scenario: Reading workspace identity
|
||||
- **WHEN** OpenSpec reads or writes workspace identity and workspace state
|
||||
- **THEN** it SHALL use `.openspec-workspace/`
|
||||
|
||||
#### Scenario: Preserving repo-local OpenSpec projects
|
||||
- **GIVEN** a repo-local OpenSpec project uses `openspec/`
|
||||
- **WHEN** that repo is linked to a workspace
|
||||
- **THEN** OpenSpec SHALL continue treating `openspec/` as that repo's local OpenSpec directory
|
||||
- **AND** workspace planning SHALL remain anchored in the workspace root
|
||||
|
||||
#### Scenario: Avoiding repo-local initialization in the workspace root
|
||||
- **WHEN** a user is working from an OpenSpec workspace root
|
||||
- **THEN** OpenSpec SHALL treat that root as a workspace coordination surface
|
||||
- **AND** users SHALL not need to initialize a repo-local `openspec/` project inside the workspace root
|
||||
|
||||
### Requirement: Safe Workspace Sharing
|
||||
OpenSpec SHALL keep shared workspace information separate from local machine paths.
|
||||
|
||||
#### Scenario: Sharing workspace planning
|
||||
- **WHEN** a workspace is shared with another user or machine
|
||||
- **THEN** shared workspace information SHALL include portable workspace identity and stable link names
|
||||
- **AND** it SHALL not require another user to reuse the original user's absolute checkout paths
|
||||
|
||||
#### Scenario: Keeping checkout paths local
|
||||
- **WHEN** OpenSpec stores local paths for a workspace
|
||||
- **THEN** those paths SHALL be treated as local to the current machine and runtime
|
||||
- **AND** another machine MAY map the same link names to different local paths
|
||||
|
||||
#### Scenario: Preserving runtime-local paths
|
||||
- **WHEN** OpenSpec reads or writes local workspace paths
|
||||
- **THEN** it SHALL preserve path strings valid for the current runtime
|
||||
- **AND** it SHALL support native Windows paths and WSL2/Linux paths as local state values
|
||||
|
||||
#### Scenario: Excluding local state from portable collaboration
|
||||
- **WHEN** OpenSpec creates a workspace
|
||||
- **THEN** it SHALL exclude `.openspec-workspace/local.yaml` from portable collaboration state by default
|
||||
- **AND** `.openspec-workspace/workspace.yaml` SHALL remain the portable workspace identity and link-name state
|
||||
|
||||
### Requirement: Standard Workspace Location
|
||||
OpenSpec SHALL use a standard location for OpenSpec-managed workspaces without asking most users to choose one.
|
||||
|
||||
#### Scenario: Using the standard workspace location
|
||||
- **WHEN** OpenSpec needs the location for OpenSpec-managed workspaces
|
||||
- **THEN** it SHALL use `<global-data-dir>/workspaces`
|
||||
- **AND** `<global-data-dir>` SHALL follow existing OpenSpec XDG and platform data directory behavior
|
||||
|
||||
#### Scenario: Avoiding workspace-specific storage overrides
|
||||
- **WHEN** OpenSpec resolves the location for OpenSpec-managed workspaces
|
||||
- **THEN** it SHALL not use a workspace-specific environment variable, command, or configuration setting in this slice
|
||||
- **AND** managed workspace storage SHALL remain under `<global-data-dir>/workspaces`
|
||||
|
||||
#### Scenario: Running from native Windows
|
||||
- **WHEN** OpenSpec runs from native Windows shells such as PowerShell
|
||||
- **AND** `XDG_DATA_HOME` is not set
|
||||
- **THEN** OpenSpec SHALL store managed workspaces under the Windows global data location
|
||||
- **AND** paths SHALL follow native Windows path behavior
|
||||
|
||||
#### Scenario: Running from WSL2
|
||||
- **WHEN** OpenSpec runs from WSL2
|
||||
- **THEN** OpenSpec SHALL store managed workspaces under the Linux/XDG data location inside WSL
|
||||
- **AND** paths SHALL follow Linux path behavior inside WSL
|
||||
|
||||
#### Scenario: Using the workspace location automatically
|
||||
- **WHEN** OpenSpec creates or resolves OpenSpec-managed workspaces in later workflows
|
||||
- **THEN** it SHALL use the resolved workspace location by default
|
||||
- **AND** users SHALL be able to follow the normal workspace flow without choosing a storage location
|
||||
|
||||
#### Scenario: Showing the workspace path
|
||||
- **WHEN** OpenSpec creates a workspace in the standard workspace location
|
||||
- **THEN** it SHALL report the workspace path to the user
|
||||
- **AND** it SHALL not hide where planning files were created
|
||||
|
||||
#### Scenario: Staying in the current runtime
|
||||
- **WHEN** OpenSpec resolves workspace paths or local repo paths
|
||||
- **THEN** it SHALL interpret paths for the runtime running OpenSpec
|
||||
- **AND** Windows, UNC WSL, and WSL mount paths SHALL remain explicit user-provided paths
|
||||
|
||||
### Requirement: Local Workspace Registry
|
||||
OpenSpec SHALL keep a lightweight local registry of known workspaces on the current machine.
|
||||
|
||||
#### Scenario: Recording known workspaces
|
||||
- **WHEN** OpenSpec creates or learns about a managed workspace
|
||||
- **THEN** it SHALL be able to record the workspace name and path in a local registry
|
||||
- **AND** the registry SHALL be machine-local state
|
||||
|
||||
#### Scenario: Keeping workspace folders authoritative
|
||||
- **WHEN** OpenSpec reads workspace details
|
||||
- **THEN** each workspace folder's `.openspec-workspace/workspace.yaml` SHALL remain the source of truth for that workspace
|
||||
- **AND** the local registry SHALL act only as an index of known workspace paths
|
||||
|
||||
#### Scenario: Finding workspaces from anywhere
|
||||
- **WHEN** a later workspace command runs outside a workspace directory
|
||||
- **THEN** OpenSpec MAY use the local registry to find known workspaces
|
||||
- **AND** commands that need one workspace MAY use the registry to support an interactive picker
|
||||
|
||||
### Requirement: Stable Link Names
|
||||
OpenSpec SHALL use stable link names to refer to repos and folders in workspace planning.
|
||||
|
||||
#### Scenario: Referring to a repo or folder in workspace planning
|
||||
- **WHEN** workspace state or later workspace planning artifacts refer to a linked repo or folder
|
||||
- **THEN** they SHALL use the stable link name
|
||||
- **AND** the same link name SHALL remain valid even when local checkout paths differ
|
||||
|
||||
#### Scenario: Reusing link names across machines
|
||||
- **WHEN** a workspace is used on another machine
|
||||
- **THEN** link names SHALL remain stable
|
||||
- **AND** local checkout paths MAY differ on that machine
|
||||
|
||||
#### Scenario: Rejecting invalid link names
|
||||
- **WHEN** OpenSpec accepts a workspace link name
|
||||
- **THEN** it SHALL reject empty names, `.` or `..`, and names containing path separators
|
||||
- **AND** link names SHALL be unique within the workspace
|
||||
|
||||
### Requirement: Linked Repos And Folders
|
||||
OpenSpec SHALL allow workspace planning to include linked repos and folders before they have repo-local OpenSpec state.
|
||||
|
||||
#### Scenario: Planning with a repo that has not adopted OpenSpec
|
||||
- **WHEN** a workspace links a repo path that does not yet contain repo-local `openspec/`
|
||||
- **THEN** the repo SHALL still be available for workspace-level planning
|
||||
- **AND** implementation readiness MAY be handled by a later workflow
|
||||
|
||||
#### Scenario: Planning across monorepo folders
|
||||
- **WHEN** planning spans multiple packages, services, apps, or directories inside one monorepo
|
||||
- **THEN** the workspace SHALL be able to link those folders separately
|
||||
- **AND** each folder SHALL not need its own repo-local `openspec/` directory to participate in workspace planning
|
||||
|
||||
#### Scenario: Treating repos and folders consistently
|
||||
- **WHEN** a workspace plan includes both separate repos and folders inside a monorepo
|
||||
- **THEN** OpenSpec SHALL use the same planning model for both
|
||||
- **AND** users SHALL not need to create different kinds of workspace plans for multi-repo and monorepo changes
|
||||
|
||||
#### Scenario: Recording links without changing targets
|
||||
- **WHEN** OpenSpec records a link between a workspace and a local repo or folder
|
||||
- **THEN** it SHALL store the link in workspace state
|
||||
- **AND** it SHALL not create, copy, move, initialize, or edit files inside the linked repo or folder
|
||||
|
||||
### Requirement: Planning Before Implementation
|
||||
OpenSpec SHALL treat workspace creation and detection as planning setup, not implementation.
|
||||
|
||||
#### Scenario: Creating or detecting a workspace
|
||||
- **WHEN** a workspace exists
|
||||
- **THEN** OpenSpec SHALL treat it as a place for workspace-level planning
|
||||
- **AND** repo implementation files SHALL remain unchanged until an explicit implementation workflow runs
|
||||
|
||||
#### Scenario: Deferring repo implementation
|
||||
- **WHEN** repo-local implementation, apply, verify, or archive behavior is needed
|
||||
- **THEN** that behavior SHALL require an explicit later workspace workflow
|
||||
|
||||
### Requirement: Repo Ownership Boundaries
|
||||
OpenSpec SHALL keep repo ownership legible when planning happens in a workspace.
|
||||
|
||||
#### Scenario: Planning across owned repos
|
||||
- **WHEN** a workspace plan refers to behavior owned by a repo or source area
|
||||
- **THEN** that owner SHALL remain the home for canonical specs and implementation work
|
||||
- **AND** the workspace SHALL make the cross-boundary plan visible without taking ownership away from that owner
|
||||
|
||||
#### Scenario: Drafting before ownership is clear
|
||||
- **WHEN** cross-repo behavior is still being explored and ownership is not clear
|
||||
- **THEN** the workspace MAY hold planning notes or draft behavior
|
||||
- **AND** those drafts SHALL remain distinguishable from canonical repo-owned specs
|
||||
@@ -0,0 +1,56 @@
|
||||
## 1. POC Findings And Model Decisions
|
||||
|
||||
- [x] 1.1 Capture the foundation POC findings in the proposal/design artifacts
|
||||
- [x] 1.2 Settle `.openspec-workspace/` as the workspace metadata directory
|
||||
- [x] 1.3 Define the minimal workspace root shape and root marker
|
||||
- [x] 1.4 Define committed workspace state versus machine-local workspace state
|
||||
- [x] 1.5 Capture that workspace setup is useful only after at least one repo or folder is linked
|
||||
- [x] 1.6 Capture that repo-owned specs and implementation remain owned by repos
|
||||
- [x] 1.7 Capture that planning can include repos or monorepo folders without repo-local OpenSpec state
|
||||
- [x] 1.8 Capture that workspaces hold many changes and are not feature containers
|
||||
- [x] 1.9 Capture `link`/`relink` as the user-facing model instead of `add-repo`/`update-repo`
|
||||
|
||||
## 2. Foundation Helpers
|
||||
|
||||
- [x] 2.1 Add workspace path constants and helpers for `.openspec-workspace/`, `workspace.yaml`, `local.yaml`, and root `changes/`
|
||||
- [x] 2.2 Add workspace root detection from an arbitrary starting directory
|
||||
- [x] 2.3 Add typed parsing and validation for minimal shared workspace state
|
||||
- [x] 2.4 Add typed parsing and validation for minimal machine-local workspace state
|
||||
- [x] 2.5 Ensure repo-local `openspec/` projects are not mistaken for coordination workspaces
|
||||
- [x] 2.6 Add a standard workspace location resolver using `getGlobalDataDir()/workspaces`
|
||||
- [x] 2.7 Ensure workspace path helpers use platform path APIs and avoid hardcoded POSIX separators
|
||||
- [x] 2.8 Add local workspace registry path constants and helpers
|
||||
|
||||
## 3. Metadata And Local State
|
||||
|
||||
- [x] 3.1 Define the versioned shared-state shape with workspace name and stable link map
|
||||
- [x] 3.2 Define the versioned local-state shape with stable link names mapped to local paths
|
||||
- [x] 3.3 Ensure local-state files are treated as machine-local and OpenSpec-created workspaces exclude `.openspec-workspace/local.yaml` from portable collaboration state
|
||||
- [x] 3.4 Add validation for invalid versions, invalid link names, malformed link maps, and malformed local path maps
|
||||
- [x] 3.5 Preserve native Windows and WSL2 path strings when reading and writing local path state
|
||||
- [x] 3.6 Define the versioned local registry shape with workspace names mapped to workspace roots
|
||||
- [x] 3.7 Ensure the local registry is treated as a convenience index, not the workspace source of truth
|
||||
|
||||
## 4. Documentation And Guidance
|
||||
|
||||
- [x] 4.1 Document the coordination workspace mental model
|
||||
- [x] 4.2 Document how `.openspec-workspace/` differs from repo-local `openspec/`
|
||||
- [x] 4.3 Document stable link names as the way to refer to linked repos and folders
|
||||
- [x] 4.4 Document which behavior is intentionally deferred to later workspace slices
|
||||
- [x] 4.5 Document native Windows/PowerShell and WSL2 path behavior for managed workspace storage
|
||||
- [x] 4.6 Document linked repos/folders without repo-local OpenSpec and large-monorepo planning behavior
|
||||
- [x] 4.7 Document the local workspace registry and global command model
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- [x] 5.1 Add unit tests for root detection and non-detection cases
|
||||
- [x] 5.2 Add unit tests for shared-state and local-state parsing
|
||||
- [x] 5.3 Add unit tests for standard workspace location resolution with XDG/Linux fallback and native Windows fallback
|
||||
- [x] 5.4 Add unit tests that local-state parsing preserves native Windows and WSL2-style paths
|
||||
- [x] 5.5 Add unit tests for repo-local compatibility boundaries
|
||||
- [x] 5.6 Add tests or docs coverage that linked repos/folders do not require repo-local `openspec/`
|
||||
- [x] 5.7 Add tests or docs coverage for monorepo folder links under the same workspace model
|
||||
- [x] 5.8 Add tests for local registry parsing and stale registry entries
|
||||
- [x] 5.9 Add tests or docs coverage for `.openspec-workspace/local.yaml` exclusion in OpenSpec-created workspaces
|
||||
- [x] 5.10 Run `openspec validate workspace-foundation --strict`
|
||||
- [x] 5.11 Run targeted test coverage for the new workspace foundation helpers
|
||||
@@ -0,0 +1,356 @@
|
||||
## Product Shape
|
||||
|
||||
This slice is the first user-facing step after `workspace-foundation`.
|
||||
|
||||
The user experience should be:
|
||||
|
||||
```text
|
||||
I set up a workspace.
|
||||
I link the repos or folders it should know about.
|
||||
I can list my workspaces later.
|
||||
I can ask OpenSpec what is broken and how to fix it.
|
||||
```
|
||||
|
||||
No change proposal is required yet.
|
||||
|
||||
## Links
|
||||
|
||||
A workspace link is a stable name plus a local path on the current machine.
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
api -> /repos/api
|
||||
web -> /repos/web
|
||||
checkout -> /repos/platform/apps/checkout
|
||||
billing -> /repos/platform/services/billing
|
||||
```
|
||||
|
||||
The path may point at a full repo or a folder inside a large monorepo. It may point at a repo or folder that has not adopted repo-local OpenSpec yet.
|
||||
|
||||
The product language should say "repos or folders". It should avoid "working set", "code area", "entry", "alias", and "local overlay" in user-facing output.
|
||||
|
||||
Path handling should behave like a folder picker. The user may type a relative or absolute path, but OpenSpec should verify that it points to an existing folder, convert it to an absolute path relative to the command's current working directory when needed, and store that verified absolute path in local workspace state. OpenSpec should not store the raw string the user typed.
|
||||
|
||||
Path conversion stays in the current runtime. Native Windows paths, WSL2 paths, and Unix paths should not be translated across runtimes. Where duplicate-path detection needs canonical comparisons, OpenSpec may compare canonical existing paths internally, but it should store and display the verified absolute path for the current runtime.
|
||||
|
||||
## Names
|
||||
|
||||
Workspace names should be kebab-case:
|
||||
|
||||
```text
|
||||
platform
|
||||
checkout-web
|
||||
api2
|
||||
```
|
||||
|
||||
Invalid workspace names include uppercase letters, underscores, dots, spaces, leading hyphens, trailing hyphens, empty names, dot names, and path separators. Interactive setup should explain the expected form and let the user retry. Non-interactive setup should fail with the same expectation in the error message.
|
||||
|
||||
Link names should keep the folder-style validation from `workspace-foundation`: they must not be empty, must not be `.` or `..`, must not contain path separators, and must be unique inside the workspace. This lets inferred link names match existing folder basenames without forcing users to rename local folders for workspace planning.
|
||||
|
||||
Link names are normally inferred from the folder basename:
|
||||
|
||||
```text
|
||||
/repos/api -> api
|
||||
/repos/platform/apps/checkout -> checkout
|
||||
```
|
||||
|
||||
If the inferred name conflicts, interactive setup should show the conflicting name and the existing path it maps to, then ask for a different name. Non-interactive setup and direct `workspace link` should fail with a clear message instead of silently overwriting.
|
||||
|
||||
Duplicate-name errors should be specific:
|
||||
|
||||
```text
|
||||
Cannot use link name 'api' because another link already uses that name.
|
||||
Existing link:
|
||||
api -> /repos/api
|
||||
|
||||
Choose a different name:
|
||||
openspec workspace link archived-api /archive/api
|
||||
|
||||
If you meant to change the existing link path:
|
||||
openspec workspace relink api /archive/api
|
||||
```
|
||||
|
||||
This slice does not add a separate link-rename command. Renaming a link can be considered later if users need it, but v1 should keep the command model crisp: `link` adds a new link, and `relink` changes the local path for an existing link.
|
||||
|
||||
## Commands
|
||||
|
||||
### `workspace setup`
|
||||
|
||||
Guided onboarding:
|
||||
|
||||
- create a workspace in the standard workspace location
|
||||
- ask for a workspace name
|
||||
- require at least one existing repo or folder path
|
||||
- infer link names from folder names
|
||||
- let the user add more repos or folders with a simple repeated prompt
|
||||
- record the workspace in the local workspace registry
|
||||
- run `workspace doctor`
|
||||
- print the workspace location, planning path, linked repos or folders, and next useful commands
|
||||
|
||||
This slice should not ask for preferred agent or open the workspace with an agent. Those belong to `workspace-open-agent-context`.
|
||||
|
||||
Setup should support a non-interactive mode for automation:
|
||||
|
||||
```bash
|
||||
openspec workspace setup --no-interactive --name platform --link /path/to/api --link web=/path/to/web
|
||||
```
|
||||
|
||||
In non-interactive mode, setup should fail cleanly unless the user provides a valid workspace name and at least one valid link. `--link` should accept either a path, which infers the name from the folder basename, or `name=path`.
|
||||
|
||||
There is no public `workspace create` command in this slice. Setup is the creation flow.
|
||||
|
||||
### `workspace list`
|
||||
|
||||
Show known OpenSpec-managed workspaces from the local workspace registry.
|
||||
|
||||
`workspace ls` should behave the same way.
|
||||
|
||||
The output should answer what exists and what each workspace links to:
|
||||
|
||||
```yaml
|
||||
workspaces:
|
||||
- name: platform
|
||||
location: /.../openspec/workspaces/platform
|
||||
links:
|
||||
- name: api
|
||||
path: /repos/api
|
||||
- name: web
|
||||
path: /repos/web
|
||||
- name: checkout
|
||||
location: /.../openspec/workspaces/checkout
|
||||
links:
|
||||
- name: app
|
||||
path: /repos/platform/apps/checkout
|
||||
```
|
||||
|
||||
List should keep deep validation for `workspace doctor`. It can still report obviously stale workspace registry entries if a known workspace location no longer exists. Stale registry entries are report-only in this slice: `workspace list` should not delete, rewrite, or repair registry entries, and this slice should not add a `workspace forget` command.
|
||||
|
||||
For JSON output, list should use typed workspace objects with a structured `status` array for issues:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaces": [
|
||||
{
|
||||
"name": "platform",
|
||||
"root": "/.../openspec/workspaces/platform",
|
||||
"links": [
|
||||
{
|
||||
"name": "api",
|
||||
"path": "/repos/api",
|
||||
"status": []
|
||||
}
|
||||
],
|
||||
"status": []
|
||||
},
|
||||
{
|
||||
"name": "old-platform",
|
||||
"root": "/.../openspec/workspaces/old-platform",
|
||||
"links": [],
|
||||
"status": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "workspace_root_missing",
|
||||
"message": "Workspace location does not exist.",
|
||||
"fix": "Remove or repair the local registry entry."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": []
|
||||
}
|
||||
```
|
||||
|
||||
### `workspace link [name] <path>`
|
||||
|
||||
Record an existing repo or folder path for the selected workspace.
|
||||
|
||||
Supported forms:
|
||||
|
||||
```bash
|
||||
openspec workspace link /path/to/api
|
||||
openspec workspace link api-service /path/to/api
|
||||
```
|
||||
|
||||
The one-argument form infers the link name from the folder basename. The two-argument form lets the user choose the link name.
|
||||
|
||||
The path must exist. The command should accept:
|
||||
|
||||
- full repo roots
|
||||
- monorepo folders such as packages, services, and apps
|
||||
- repos or folders without repo-local `openspec/`
|
||||
|
||||
If the user passes a relative path, OpenSpec should resolve it against the command's current working directory before writing local state.
|
||||
|
||||
If the path has repo-local OpenSpec state, OpenSpec can report the repo specs path in doctor output. If it does not, OpenSpec should still allow workspace planning.
|
||||
|
||||
`workspace link` only records the link. It must not create, copy, move, initialize, or edit files in the linked repo or folder.
|
||||
|
||||
### `workspace relink <name> <path>`
|
||||
|
||||
Repair or change the local path for an existing link.
|
||||
|
||||
Relink should use the same path handling as link: require an existing folder, resolve relative inputs to absolute runtime-local paths, and store the verified path.
|
||||
|
||||
This slice should keep relink focused on path repair. It should not include owner or handoff metadata; that language was too process-heavy in the POC and can be revisited later if users need contact or notes fields.
|
||||
|
||||
### `workspace doctor`
|
||||
|
||||
Explain one selected workspace from the user's machine. If the command is run from a workspace folder or subdirectory and `--workspace <name>` is not provided, doctor should use that current workspace. Otherwise it should follow the normal workspace-selection rules.
|
||||
|
||||
Doctor should inspect:
|
||||
|
||||
- workspace location
|
||||
- workspace planning path
|
||||
- linked repos and folders
|
||||
- whether each local path exists
|
||||
- repo-local specs path when present
|
||||
- missing local paths
|
||||
- local names that are not in shared workspace state
|
||||
- shared link names that are missing local paths
|
||||
- suggested fixes for each issue
|
||||
|
||||
Doctor should not scan every known workspace in the local registry by default. Broad registry visibility belongs to `workspace list`. A future `workspace doctor --all` can be considered later if users need global workspace diagnostics.
|
||||
|
||||
Doctor should report issues and suggested fixes. It should not repair anything automatically.
|
||||
|
||||
Registry cleanup remains out of scope. If doctor cannot inspect the selected workspace because the registry points at a missing or invalid workspace location, it should report that selected-workspace issue through status entries and stop before inspecting links. Other stale registry entries should be surfaced by `workspace list`, not by selected-workspace doctor.
|
||||
|
||||
Human output should be readable by default: a short workspace summary, linked repo or folder rows, and a clear issues section when anything needs attention. It should not be raw JSON or a rigid YAML dump.
|
||||
|
||||
JSON output should follow the object/status pattern: primary data lives in typed objects, and diagnostics live in `status` arrays. A healthy object has `status: []`. Status entries should include `severity`, `code`, `message`, and optional `target` and `fix` fields.
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace": {
|
||||
"name": "platform",
|
||||
"root": "/.../openspec/workspaces/platform",
|
||||
"planning_path": "/.../openspec/workspaces/platform/changes",
|
||||
"links": [
|
||||
{
|
||||
"name": "api",
|
||||
"path": "/repos/api",
|
||||
"repo_specs_path": "/repos/api/openspec/specs",
|
||||
"status": []
|
||||
},
|
||||
{
|
||||
"name": "web",
|
||||
"path": "/old/path/web",
|
||||
"repo_specs_path": null,
|
||||
"status": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "linked_path_missing",
|
||||
"message": "Linked path does not exist.",
|
||||
"target": "links.web.path",
|
||||
"fix": "openspec workspace relink web /path/to/web"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": []
|
||||
},
|
||||
"status": []
|
||||
}
|
||||
```
|
||||
|
||||
## Workspace Selection
|
||||
|
||||
Workspace commands should work from anywhere.
|
||||
|
||||
Commands that do not need one workspace:
|
||||
|
||||
- `workspace setup`
|
||||
- `workspace list`
|
||||
- `workspace ls`
|
||||
|
||||
Commands that need one workspace:
|
||||
|
||||
- `workspace link`
|
||||
- `workspace relink`
|
||||
- `workspace doctor`
|
||||
|
||||
If the current command needs one workspace and `--workspace <name>` is not provided:
|
||||
|
||||
- use the current workspace when running from inside a workspace
|
||||
- otherwise show an interactive picker when multiple known workspaces exist
|
||||
- otherwise select the only known workspace
|
||||
- otherwise explain that no workspaces exist and suggest `openspec workspace setup`
|
||||
|
||||
The current workspace wins even if it is not in the local workspace registry. This supports manually created or shared workspace folders. In that case commands should continue and include a non-fatal warning status:
|
||||
|
||||
```json
|
||||
{
|
||||
"severity": "warning",
|
||||
"code": "workspace_not_in_local_registry",
|
||||
"message": "This workspace is not recorded in the local workspace registry.",
|
||||
"target": "workspace.root",
|
||||
"fix": "Run a mutating workspace command from this workspace, such as workspace link or workspace relink, to record it locally."
|
||||
}
|
||||
```
|
||||
|
||||
For human output, this should be a short warning rather than a blocking error. Successful mutating commands that use an unregistered current workspace, such as `workspace link` or `workspace relink`, should record the workspace name and location in the local registry after the mutation succeeds. Non-mutating commands such as `workspace doctor` should not write registry state; they should only report the warning. This slice should not add a standalone `workspace register` or `workspace join` command.
|
||||
|
||||
In non-interactive mode, commands that need one workspace should fail when selection is ambiguous and suggest `--workspace <name>`.
|
||||
|
||||
`--json` should also suppress prompting for commands that need one workspace. If a command would otherwise show a picker, JSON mode should fail with a structured status error and suggest `--workspace <name>`.
|
||||
|
||||
## Machine-Local Files
|
||||
|
||||
Workspace creation should make machine-local state safe by default.
|
||||
|
||||
The workspace should ignore:
|
||||
|
||||
```text
|
||||
/.openspec-workspace/local.yaml
|
||||
```
|
||||
|
||||
The local workspace registry should also be machine-local:
|
||||
|
||||
```text
|
||||
<global-data-dir>/workspaces/registry.yaml
|
||||
```
|
||||
|
||||
Generated agent launch surfaces can be ignored by `workspace-open-agent-context` when that slice creates them.
|
||||
|
||||
## JSON Output
|
||||
|
||||
Interactive setup does not need JSON output as its primary contract. Non-interactive setup and direct commands should support JSON output for scripting:
|
||||
|
||||
- `workspace setup --no-interactive --json`
|
||||
- `workspace list --json`
|
||||
- `workspace link --json`
|
||||
- `workspace relink --json`
|
||||
- `workspace doctor --json`
|
||||
|
||||
`workspace setup --json` should require `--no-interactive`. If a user runs `workspace setup --json` without `--no-interactive`, setup should fail clearly because an interactive wizard cannot produce clean JSON. Direct commands such as `workspace list --json`, `workspace link --json`, `workspace relink --json`, and `workspace doctor --json` do not require `--no-interactive`, but JSON mode should disable prompts and fail on ambiguous workspace selection.
|
||||
|
||||
JSON output should use object/status structure across commands:
|
||||
|
||||
- primary entities such as `workspace`, `workspaces`, or `link` carry the durable data
|
||||
- `status` arrays carry warnings, errors, and suggested fixes
|
||||
- status entries use stable `code` values plus human-readable `message` text
|
||||
- command-level `status` describes the whole response
|
||||
- object-level `status` describes that specific workspace or link
|
||||
|
||||
## POC Adjustments
|
||||
|
||||
Keep:
|
||||
|
||||
- guided setup as the default first run
|
||||
- direct list/link/check commands
|
||||
- shared state separate from local paths
|
||||
- clean non-interactive failure when required setup inputs are missing
|
||||
- JSON output for non-interactive/direct commands
|
||||
|
||||
Change:
|
||||
|
||||
- do not expose public `workspace create` in the first release
|
||||
- do not require repo-local OpenSpec state to link a repo or folder
|
||||
- use `workspace link` instead of `workspace add-repo`
|
||||
- use `workspace relink` instead of `workspace update-repo`
|
||||
- do not save a preferred agent during setup
|
||||
- do not offer to open the workspace from setup
|
||||
- require setup to link at least one existing repo or folder
|
||||
- keep relink behavior focused on path repair rather than owner or handoff metadata
|
||||
- do not use "working set", "code area", "entry", "alias", or "local overlay" in human-facing output
|
||||
@@ -0,0 +1,128 @@
|
||||
## Why
|
||||
|
||||
Note: the change id keeps the older "register repos" wording for continuity. User-facing product language in this slice is `workspace setup`, `workspace link`, `workspace relink`, and "linked repos or folders."
|
||||
|
||||
Users start workspace work by creating a planning home and linking the repos or folders OpenSpec should know about.
|
||||
|
||||
They should not have to create a change before OpenSpec can see the relevant repos, monorepo folders, packages, services, or apps.
|
||||
|
||||
The product rule is:
|
||||
|
||||
```text
|
||||
Workspace visibility is not change commitment.
|
||||
```
|
||||
|
||||
A workspace is the durable planning home. A change is a feature, fix, project, or other planned piece of work inside that workspace.
|
||||
|
||||
## What Changes
|
||||
|
||||
Add the first user-facing workspace setup flow:
|
||||
|
||||
```text
|
||||
Set up a workspace.
|
||||
Link existing repos or folders.
|
||||
List known workspaces and what they link to.
|
||||
Check what OpenSpec can resolve and how to fix problems.
|
||||
```
|
||||
|
||||
Expected user surface:
|
||||
|
||||
```bash
|
||||
openspec workspace setup
|
||||
openspec workspace setup --no-interactive --name platform --link /path/to/api --link web=/path/to/web
|
||||
openspec workspace list
|
||||
openspec workspace ls
|
||||
openspec workspace link /path/to/api
|
||||
openspec workspace link api-service /path/to/api
|
||||
openspec workspace relink api /new/path/to/api
|
||||
openspec workspace doctor
|
||||
```
|
||||
|
||||
`workspace setup` is the creation path for users. It should ask for the workspace name first, create the workspace in the standard location, require at least one existing repo or folder path, infer link names from folder names, show the workspace location, and run a check at the end so the user knows what OpenSpec can see.
|
||||
|
||||
Workspace names should be kebab-case so they are clean managed-folder names and stable registry identifiers. Link names should keep the folder-style validation from `workspace-foundation` because they are often inferred directly from existing repo or folder basenames.
|
||||
|
||||
`workspace setup --no-interactive` is the automation path. It should require enough flags to create a useful workspace, including a workspace name and at least one link.
|
||||
|
||||
`workspace list` shows known OpenSpec-managed workspaces from the local workspace registry, including each workspace location and linked repos or folders.
|
||||
|
||||
`workspace link` records an existing local repo or folder path for the selected workspace. It should support a simple form that infers the link name from the folder name and an explicit-name form for conflicts or clarity. Linking does not create, copy, move, initialize, or edit files in the linked repo or folder.
|
||||
|
||||
Linking should behave like selecting a folder from a picker: OpenSpec verifies the folder exists, resolves relative inputs to an absolute path in the current runtime, and stores that verified path instead of the raw input string.
|
||||
|
||||
When a link name is already in use, OpenSpec should preserve the existing link and show the conflicting name with the existing path. The error should suggest choosing a different link name, or using `workspace relink <name> <path>` if the user intended to change the existing link's path.
|
||||
|
||||
`workspace relink` lets users repair or change the local path for an existing link without recreating the workspace. It should not introduce owner or handoff metadata in this slice.
|
||||
|
||||
`workspace doctor` explains what the current machine can resolve for one selected workspace: the workspace location, the workspace planning path, linked repos or folders, missing paths, repo-local specs paths when present, and suggested fixes. It should infer the current workspace when run from inside a workspace. It reports issues but does not repair them automatically.
|
||||
|
||||
Workspace commands should work globally. When a command needs one workspace and the user did not specify it, OpenSpec should use the local registry to show an interactive picker. In non-interactive mode, it should fail with a clear message and suggest `--workspace <name>`.
|
||||
|
||||
When a command runs from inside a valid workspace that is not in the local registry, OpenSpec should still use that current workspace. It should surface a non-fatal warning status that the workspace is not known locally, and successful mutating commands such as `workspace link` or `workspace relink` should record that workspace in the local registry after they update workspace state.
|
||||
|
||||
Machine-readable output should separate workspace or link objects from status entries. Status should be an array of structured issues instead of scattering fields such as `root_status`, `issue`, or `fix` through the primary object shape.
|
||||
|
||||
Interactive behavior should be disabled whenever output must be script-safe. `--no-interactive` means no prompts, and `--json` should fail instead of prompting when selection or setup inputs are ambiguous. `workspace setup --json` should require `--no-interactive` so JSON setup always uses the explicit automation path.
|
||||
|
||||
Planning dependency:
|
||||
|
||||
- Depends on `workspace-foundation`.
|
||||
|
||||
## POC Findings
|
||||
|
||||
Behavior to preserve:
|
||||
|
||||
- `workspace setup` was the friendly onboarding path.
|
||||
- `workspace list` made managed workspaces discoverable.
|
||||
- A direct automation path is still useful, but it should live under `workspace setup --no-interactive`.
|
||||
- Link repair is useful, but owner or handoff metadata should not carry forward in this slice.
|
||||
- `workspace doctor` was the right place to answer "what does OpenSpec know about this workspace?"
|
||||
- Shared workspace state and local paths were stored separately.
|
||||
- Setup failed cleanly when non-interactive inputs were incomplete.
|
||||
- Created workspaces excluded machine-local path state from portable workspace state.
|
||||
|
||||
Behavior to change:
|
||||
|
||||
- The POC required linked repo paths to already contain repo-local `openspec/`. This should become an implementation-readiness signal, not a planning prerequisite.
|
||||
- The POC used repo-only language. This slice should use "repos or folders" for user-facing text.
|
||||
- The public command should be `workspace link`, not `workspace add-repo`.
|
||||
- The repair command should be `workspace relink`, not `workspace update-repo`.
|
||||
- Public `workspace create` should be removed for the first release. Setup should be the creation flow.
|
||||
- The POC's `setup` flow stored preferred agent and open behavior. Agent launch preferences belong to `workspace-open-agent-context`, not this slice.
|
||||
- Human output should avoid implementation terms such as working set, code area, entry, alias, or local overlay.
|
||||
- `setup` should require at least one linked repo or folder so the created workspace is immediately useful.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- No public `openspec workspace create` command in this first release.
|
||||
- No agent launch or workspace open behavior.
|
||||
- No preferred agent prompts or saved agent preference.
|
||||
- No owner or handoff metadata fields.
|
||||
- No workspace change creation or target selection.
|
||||
- No apply, verify, archive, branch, or worktree behavior.
|
||||
- No requirement that linked repos or folders have repo-local OpenSpec state.
|
||||
- No automatic repair behavior in `workspace doctor`.
|
||||
- No registry cleanup command such as `workspace forget`; stale registry entries are report-only in this slice.
|
||||
- No standalone `workspace register` or `workspace join` command; unregistered current workspaces are usable, and mutating workspace commands can record them locally.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `workspace-links`: Lets users set up a workspace, link repos or folders, list known workspaces, and check workspace resolution before change creation.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `cli-artifact-workflow`: Introduces workspace setup commands that happen before change creation.
|
||||
- `workspace-foundation`: Tightens workspace names to kebab-case while keeping folder-style link names.
|
||||
|
||||
## Impact
|
||||
|
||||
- `openspec workspace setup`
|
||||
- `openspec workspace list`
|
||||
- `openspec workspace ls`
|
||||
- `openspec workspace link`
|
||||
- `openspec workspace relink`
|
||||
- `openspec workspace doctor`
|
||||
- Local workspace registry usage from `workspace-foundation`.
|
||||
- Docs and generated guidance that explain linked repos or folders as planning context, not implementation commitment.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace Setup Commands
|
||||
The CLI artifact workflow SHALL expose workspace setup commands before change creation.
|
||||
|
||||
#### Scenario: Preparing workspace planning before a change
|
||||
- **WHEN** a user needs to prepare workspace planning across repos or folders
|
||||
- **THEN** the CLI SHALL provide commands to set up, list, link, relink, and doctor workspaces
|
||||
- **AND** those commands SHALL not require an active workspace change
|
||||
|
||||
#### Scenario: Listing workspaces with a short command
|
||||
- **WHEN** a user wants a concise workspace list command
|
||||
- **THEN** the CLI SHALL support `openspec workspace ls`
|
||||
- **AND** it SHALL behave the same as `openspec workspace list`
|
||||
|
||||
#### Scenario: Keeping setup separate from agent launch
|
||||
- **WHEN** a user completes workspace setup
|
||||
- **THEN** the setup workflow SHALL leave agent launch and workspace open behavior to a later workflow
|
||||
- **AND** setup SHALL not require a preferred agent choice
|
||||
|
||||
#### Scenario: Avoiding public direct creation
|
||||
- **WHEN** users create a workspace in the first workspace setup flow
|
||||
- **THEN** the CLI SHALL use `openspec workspace setup`
|
||||
- **AND** it SHALL not expose `openspec workspace create` as the public creation path
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Stable Workspace Name
|
||||
OpenSpec SHALL use one kebab-case workspace name across workspace identity, managed storage, and the local registry.
|
||||
|
||||
#### Scenario: Using one workspace name
|
||||
- **WHEN** OpenSpec creates or records a managed workspace
|
||||
- **THEN** the workspace name SHALL be stored in `.openspec-workspace/workspace.yaml`
|
||||
- **AND** the same name SHALL be used as the default managed workspace folder name
|
||||
- **AND** the same name SHALL be used as the local registry name
|
||||
|
||||
#### Scenario: Rejecting invalid workspace names
|
||||
- **WHEN** OpenSpec accepts a workspace name
|
||||
- **THEN** it SHALL require kebab-case names using lowercase letters, numbers, and single hyphen separators
|
||||
- **AND** it SHALL reject empty names, dot names, names with leading or trailing hyphens, names with repeated hyphens, uppercase letters, spaces, underscores, dots, and path separators
|
||||
- **AND** setup flows SHALL report OS-level folder creation failures clearly
|
||||
|
||||
### Requirement: Stable Link Names
|
||||
OpenSpec SHALL use stable folder-style link names to refer to repos and folders in workspace planning.
|
||||
|
||||
#### Scenario: Referring to a repo or folder in workspace planning
|
||||
- **WHEN** workspace state or later workspace planning artifacts refer to a linked repo or folder
|
||||
- **THEN** they SHALL use the stable link name
|
||||
- **AND** the same link name SHALL remain valid even when local checkout paths differ
|
||||
|
||||
#### Scenario: Reusing link names across machines
|
||||
- **WHEN** a workspace is used on another machine
|
||||
- **THEN** link names SHALL remain stable
|
||||
- **AND** local checkout paths MAY differ on that machine
|
||||
|
||||
#### Scenario: Rejecting invalid link names
|
||||
- **WHEN** OpenSpec accepts a workspace link name
|
||||
- **THEN** it SHALL reject empty names, `.` or `..`, and names containing path separators
|
||||
- **AND** link names SHALL be unique within the workspace
|
||||
- **AND** link names SHALL not be required to use workspace-name kebab-case
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Guided Workspace Setup
|
||||
OpenSpec SHALL provide a guided setup flow for users starting workspace planning.
|
||||
|
||||
#### Scenario: Creating a workspace through setup
|
||||
- **WHEN** a user runs `openspec workspace setup`
|
||||
- **THEN** OpenSpec SHALL guide the user through creating an OpenSpec workspace
|
||||
- **AND** the workspace SHALL use the standard workspace location from the workspace foundation
|
||||
|
||||
#### Scenario: Asking for the workspace name first
|
||||
- **WHEN** interactive setup starts
|
||||
- **THEN** OpenSpec SHALL ask for the workspace name before asking for repos or folders
|
||||
- **AND** workspace names SHALL use kebab-case with lowercase letters, numbers, and hyphens
|
||||
|
||||
#### Scenario: Retrying an invalid workspace name during setup
|
||||
- **WHEN** an interactive user enters an invalid workspace name
|
||||
- **THEN** OpenSpec SHALL explain that workspace names must be kebab-case
|
||||
- **AND** it SHALL let the user enter another workspace name before continuing setup
|
||||
|
||||
#### Scenario: Linking a required first repo or folder
|
||||
- **WHEN** setup asks for repos or folders
|
||||
- **THEN** the user SHALL provide at least one existing repo or folder path
|
||||
- **AND** setup SHALL not finish successfully until at least one path is linked
|
||||
|
||||
#### Scenario: Inferring link names during setup
|
||||
- **WHEN** the user provides a repo or folder path during setup
|
||||
- **THEN** OpenSpec SHALL infer the link name from the folder basename
|
||||
- **AND** it SHALL ask for a different name only when the inferred name conflicts
|
||||
|
||||
#### Scenario: Handling inferred link name conflicts during setup
|
||||
- **GIVEN** setup infers a link name that already exists in the workspace
|
||||
- **WHEN** setup is interactive
|
||||
- **THEN** OpenSpec SHALL show the conflicting link name and the existing path for that link
|
||||
- **AND** it SHALL ask the user for a different link name before continuing
|
||||
|
||||
#### Scenario: Preserving folder-style link names
|
||||
- **WHEN** OpenSpec accepts a workspace link name
|
||||
- **THEN** it SHALL allow folder-style names that are valid under the workspace foundation link-name rules
|
||||
- **AND** it SHALL not require link names to use the stricter workspace-name kebab-case rule
|
||||
|
||||
#### Scenario: Adding multiple repos or folders during setup
|
||||
- **WHEN** setup links a repo or folder
|
||||
- **THEN** OpenSpec SHALL let the user add another repo or folder with a simple repeated prompt
|
||||
- **AND** each linked path SHALL be recorded without editing the target repo or folder
|
||||
|
||||
#### Scenario: Storing verified absolute paths during setup
|
||||
- **WHEN** setup links a repo or folder path
|
||||
- **THEN** OpenSpec SHALL verify that the path resolves to an existing folder
|
||||
- **AND** it SHALL store an absolute runtime-local path in machine-local state instead of the raw user input
|
||||
- **AND** relative inputs SHALL be resolved against the command's current working directory
|
||||
|
||||
#### Scenario: Preserving equals signs in setup link paths
|
||||
- **WHEN** non-interactive setup receives a `--link` value that resolves to an existing folder and contains `=`
|
||||
- **THEN** OpenSpec SHALL treat the full value as the path
|
||||
- **AND** it SHALL infer the link name from the folder basename
|
||||
- **AND** explicit `--link <name>=<path>` inputs SHALL preserve `=` characters inside `<path>`
|
||||
|
||||
#### Scenario: Running setup with non-interactive inputs
|
||||
- **WHEN** `openspec workspace setup --no-interactive` receives a workspace name and at least one valid link
|
||||
- **THEN** OpenSpec SHALL create the workspace without prompts
|
||||
- **AND** it SHALL support repeated `--link` values
|
||||
|
||||
#### Scenario: Non-interactive setup duplicate link names
|
||||
- **WHEN** `openspec workspace setup --no-interactive` receives two links with the same inferred or explicit name
|
||||
- **THEN** OpenSpec SHALL fail with a clear duplicate link-name error
|
||||
- **AND** the error SHALL show the conflicting link name and the first path using that name
|
||||
- **AND** it SHALL suggest using explicit `--link <name>=<path>` values with different names
|
||||
|
||||
#### Scenario: Missing non-interactive setup inputs
|
||||
- **WHEN** `openspec workspace setup --no-interactive` is missing a workspace name or link
|
||||
- **THEN** OpenSpec SHALL fail with a clear message
|
||||
- **AND** it SHALL explain which flags are required
|
||||
|
||||
#### Scenario: Finishing setup
|
||||
- **WHEN** setup finishes
|
||||
- **THEN** OpenSpec SHALL show the workspace location, planning path, and linked repos or folders
|
||||
- **AND** it SHALL check what the current machine can resolve
|
||||
|
||||
#### Scenario: Recording created workspaces locally
|
||||
- **WHEN** setup creates a workspace
|
||||
- **THEN** OpenSpec SHALL record it in the local workspace registry
|
||||
- **AND** the workspace folder SHALL remain the source of truth for workspace state
|
||||
|
||||
#### Scenario: Reusing an existing workspace name during setup
|
||||
- **GIVEN** a managed workspace already exists with the requested name
|
||||
- **WHEN** a user runs setup with that workspace name
|
||||
- **THEN** OpenSpec SHALL explain that the workspace already exists
|
||||
- **AND** it SHALL not overwrite the existing workspace
|
||||
|
||||
### Requirement: Workspace Discovery
|
||||
OpenSpec SHALL let users see the OpenSpec-managed workspaces available on the current machine.
|
||||
|
||||
#### Scenario: Listing workspaces
|
||||
- **WHEN** a user runs `openspec workspace list`
|
||||
- **THEN** OpenSpec SHALL list known managed workspaces
|
||||
- **AND** each workspace SHALL include the workspace name, workspace location, and linked repos or folders
|
||||
|
||||
#### Scenario: Using the short list command
|
||||
- **WHEN** a user runs `openspec workspace ls`
|
||||
- **THEN** OpenSpec SHALL behave the same as `openspec workspace list`
|
||||
|
||||
#### Scenario: Listing when no workspaces exist
|
||||
- **WHEN** a user runs `openspec workspace list`
|
||||
- **AND** no managed workspaces exist
|
||||
- **THEN** OpenSpec SHALL say that no workspaces were found
|
||||
- **AND** it SHALL show the user how to create one
|
||||
|
||||
#### Scenario: Listing stale registry entries
|
||||
- **WHEN** the local registry contains a workspace location that no longer exists
|
||||
- **THEN** `workspace list` SHALL report the stale workspace entry
|
||||
- **AND** it SHALL avoid silently deleting registry state
|
||||
- **AND** it SHALL avoid rewriting or repairing registry state automatically
|
||||
|
||||
#### Scenario: Avoiding registry cleanup commands
|
||||
- **WHEN** users inspect stale workspace registry entries in this slice
|
||||
- **THEN** OpenSpec SHALL treat stale entries as report-only diagnostics
|
||||
- **AND** it SHALL not expose a registry cleanup command such as `workspace forget`
|
||||
|
||||
### Requirement: Global Workspace Commands
|
||||
OpenSpec SHALL let workspace commands run from outside workspace directories.
|
||||
|
||||
#### Scenario: Selecting a workspace by flag
|
||||
- **WHEN** a command that needs one workspace receives `--workspace <name>`
|
||||
- **THEN** OpenSpec SHALL use that workspace from the local registry
|
||||
- **AND** it SHALL fail clearly if the workspace name is unknown
|
||||
|
||||
#### Scenario: Using the current workspace
|
||||
- **GIVEN** the command runs from a workspace folder or subdirectory
|
||||
- **WHEN** the command needs one workspace and no `--workspace` flag is provided
|
||||
- **THEN** OpenSpec SHALL use the current workspace
|
||||
|
||||
#### Scenario: Using an unregistered current workspace
|
||||
- **GIVEN** the command runs from a valid workspace folder or subdirectory
|
||||
- **AND** that workspace is not recorded in the local workspace registry
|
||||
- **WHEN** the command needs one workspace and no `--workspace <name>` flag is provided
|
||||
- **THEN** OpenSpec SHALL use the current workspace
|
||||
- **AND** it SHALL include a non-fatal warning status with code `workspace_not_in_local_registry`
|
||||
- **AND** the warning SHALL explain how the user can get the workspace recorded locally
|
||||
|
||||
#### Scenario: Recording an unregistered current workspace after mutation
|
||||
- **GIVEN** a mutating workspace command uses a valid current workspace that is not recorded in the local workspace registry
|
||||
- **WHEN** `workspace link` or `workspace relink` succeeds
|
||||
- **THEN** OpenSpec SHALL record the workspace name and location in the local workspace registry
|
||||
|
||||
#### Scenario: Doctor does not register current workspaces
|
||||
- **GIVEN** `workspace doctor` uses a valid current workspace that is not recorded in the local workspace registry
|
||||
- **WHEN** doctor finishes
|
||||
- **THEN** OpenSpec SHALL report the non-fatal registry warning
|
||||
- **AND** it SHALL not write registry state
|
||||
|
||||
#### Scenario: Picking from multiple workspaces
|
||||
- **GIVEN** multiple known workspaces exist
|
||||
- **WHEN** an interactive command needs one workspace and none is specified
|
||||
- **THEN** OpenSpec SHALL show a workspace picker
|
||||
- **AND** the picker SHALL include workspace names and paths
|
||||
|
||||
#### Scenario: Ambiguous non-interactive workspace selection
|
||||
- **GIVEN** multiple known workspaces exist
|
||||
- **WHEN** a non-interactive command needs one workspace and none is specified
|
||||
- **THEN** OpenSpec SHALL fail with a clear message
|
||||
- **AND** it SHALL suggest passing `--workspace <name>`
|
||||
|
||||
#### Scenario: Ambiguous JSON workspace selection
|
||||
- **GIVEN** multiple known workspaces exist
|
||||
- **WHEN** a command running with `--json` needs one workspace and none is specified
|
||||
- **THEN** OpenSpec SHALL fail without showing a picker
|
||||
- **AND** it SHALL emit a structured status error
|
||||
- **AND** it SHALL suggest passing `--workspace <name>`
|
||||
|
||||
#### Scenario: No known workspaces for a command that needs one
|
||||
- **GIVEN** no known workspaces exist in the local registry
|
||||
- **AND** the command is not running from a workspace folder or subdirectory
|
||||
- **WHEN** `workspace link`, `workspace relink`, `workspace doctor`, or another command that needs one workspace runs without `--workspace <name>`
|
||||
- **THEN** OpenSpec SHALL fail without showing a picker regardless of interactive mode
|
||||
- **AND** it SHALL print `No known OpenSpec workspaces. Run 'openspec workspace setup' first.`
|
||||
- **AND** it SHALL explain that `--workspace <name>` can be used after at least one workspace is known locally
|
||||
|
||||
### Requirement: Workspace Links
|
||||
OpenSpec SHALL let users link existing repos or folders to a workspace before creating a change.
|
||||
|
||||
#### Scenario: Linking with an inferred name
|
||||
- **WHEN** a user runs `openspec workspace link <path>`
|
||||
- **THEN** OpenSpec SHALL infer the link name from the folder basename
|
||||
- **AND** it SHALL store the verified absolute local path as machine-local state
|
||||
|
||||
#### Scenario: Linking with an explicit name
|
||||
- **WHEN** a user runs `openspec workspace link <name> <path>`
|
||||
- **THEN** OpenSpec SHALL use the explicit link name for planning
|
||||
- **AND** it SHALL store the verified absolute local path as machine-local state
|
||||
|
||||
#### Scenario: Requiring an existing path
|
||||
- **WHEN** a user links a repo or folder path
|
||||
- **THEN** the path SHALL exist on the current machine
|
||||
- **AND** OpenSpec SHALL reject missing paths with a clear message
|
||||
|
||||
#### Scenario: Resolving linked paths before storage
|
||||
- **WHEN** a user links a repo or folder path
|
||||
- **THEN** OpenSpec SHALL store the verified absolute path for the current runtime
|
||||
- **AND** relative inputs SHALL be resolved against the command's current working directory
|
||||
- **AND** OpenSpec SHALL not translate paths between native Windows, WSL2, and Unix runtimes
|
||||
|
||||
#### Scenario: Linking a monorepo folder
|
||||
- **WHEN** a user links a package, service, app, or directory inside a monorepo
|
||||
- **THEN** OpenSpec SHALL store it as a workspace link
|
||||
- **AND** it SHALL not require that folder to have its own repo-local `openspec/` directory
|
||||
|
||||
#### Scenario: Linking without repo-local OpenSpec
|
||||
- **WHEN** a user links a path that does not contain repo-local OpenSpec state
|
||||
- **THEN** OpenSpec SHALL keep that repo or folder available for workspace planning
|
||||
- **AND** it SHALL not treat missing repo-local OpenSpec state as a link failure
|
||||
|
||||
#### Scenario: Link records only
|
||||
- **WHEN** a user links a repo or folder
|
||||
- **THEN** OpenSpec SHALL record workspace state and local path state
|
||||
- **AND** it SHALL not create, copy, move, initialize, or edit files in the linked repo or folder
|
||||
|
||||
#### Scenario: Blocking link when local state is invalid
|
||||
- **GIVEN** the workspace machine-local state file exists but cannot be parsed or validated
|
||||
- **WHEN** a user runs `openspec workspace link`
|
||||
- **THEN** OpenSpec SHALL fail with status code `workspace_local_state_invalid`
|
||||
- **AND** it SHALL not rewrite shared workspace state or machine-local path state
|
||||
|
||||
#### Scenario: Reusing a link name
|
||||
- **GIVEN** a workspace already has a link with a given name
|
||||
- **WHEN** a user tries to link another path with the same name
|
||||
- **THEN** OpenSpec SHALL explain that the link name is already in use by another link
|
||||
- **AND** it SHALL show the existing link name and existing path
|
||||
- **AND** it SHALL suggest choosing a different link name
|
||||
- **AND** it SHALL suggest `workspace relink <name> <path>` when the user intended to change the existing link path
|
||||
- **AND** it SHALL preserve the existing link unless the user explicitly relinks it
|
||||
|
||||
### Requirement: Workspace Relinks
|
||||
OpenSpec SHALL let users update existing link paths without recreating the workspace.
|
||||
|
||||
#### Scenario: Updating a local path
|
||||
- **GIVEN** a workspace has a link
|
||||
- **WHEN** a user runs `openspec workspace relink <name> <path>`
|
||||
- **THEN** OpenSpec SHALL keep the stable link name
|
||||
- **AND** it SHALL update the machine-local path for the current machine to the verified absolute path
|
||||
|
||||
#### Scenario: Requiring an existing relink path
|
||||
- **WHEN** a user relinks to a new path
|
||||
- **THEN** the new path SHALL exist on the current machine
|
||||
- **AND** OpenSpec SHALL reject missing paths with a clear message
|
||||
|
||||
#### Scenario: Resolving relink paths before storage
|
||||
- **WHEN** a user relinks to a new path
|
||||
- **THEN** OpenSpec SHALL store the verified absolute path for the current runtime
|
||||
- **AND** relative inputs SHALL be resolved against the command's current working directory
|
||||
|
||||
#### Scenario: Blocking relink when local state is invalid
|
||||
- **GIVEN** the workspace machine-local state file exists but cannot be parsed or validated
|
||||
- **WHEN** a user runs `openspec workspace relink`
|
||||
- **THEN** OpenSpec SHALL fail with status code `workspace_local_state_invalid`
|
||||
- **AND** it SHALL not rewrite machine-local path state
|
||||
|
||||
#### Scenario: Updating an unknown link
|
||||
- **WHEN** a user tries to relink a link that does not exist
|
||||
- **THEN** OpenSpec SHALL explain that the link name is unknown
|
||||
- **AND** it SHALL preserve existing workspace state
|
||||
|
||||
#### Scenario: Avoiding owner and handoff fields
|
||||
- **WHEN** users link or relink repos or folders in this slice
|
||||
- **THEN** OpenSpec SHALL not ask for owner or handoff metadata
|
||||
- **AND** link maintenance SHALL focus on names and local paths
|
||||
|
||||
### Requirement: Workspace Health Check
|
||||
OpenSpec SHALL explain what the current machine can resolve for a workspace.
|
||||
|
||||
#### Scenario: Doctor checks one selected workspace
|
||||
- **WHEN** a user runs `openspec workspace doctor`
|
||||
- **THEN** OpenSpec SHALL inspect one selected workspace
|
||||
- **AND** it SHALL not scan every known workspace in the local registry by default
|
||||
|
||||
#### Scenario: Doctor infers the current workspace
|
||||
- **GIVEN** the command runs from a workspace folder or subdirectory
|
||||
- **WHEN** the user runs `openspec workspace doctor` without `--workspace <name>`
|
||||
- **THEN** OpenSpec SHALL inspect the current workspace
|
||||
|
||||
#### Scenario: Checking a healthy workspace
|
||||
- **WHEN** a user runs `openspec workspace doctor`
|
||||
- **THEN** OpenSpec SHALL show the workspace location and workspace planning path
|
||||
- **AND** it SHALL show linked repos or folders and which paths resolve on the current machine
|
||||
|
||||
#### Scenario: Selected workspace location is missing
|
||||
- **GIVEN** the selected workspace comes from the local registry
|
||||
- **AND** the registered workspace location is missing or invalid
|
||||
- **WHEN** a user runs `openspec workspace doctor`
|
||||
- **THEN** OpenSpec SHALL report a selected-workspace status error
|
||||
- **AND** it SHALL not attempt to inspect links for that workspace
|
||||
|
||||
#### Scenario: Reporting repo-local specs paths
|
||||
- **WHEN** a linked repo or folder resolves
|
||||
- **THEN** doctor SHALL report `repo_specs_path` when repo-local `openspec/specs` exists
|
||||
- **AND** it SHALL report `repo_specs_path: null` when repo-local specs are not present
|
||||
|
||||
#### Scenario: Checking missing paths
|
||||
- **WHEN** a link points to a path that is missing on the current machine
|
||||
- **THEN** doctor SHALL identify the affected link name
|
||||
- **AND** it SHALL include a suggested `workspace relink` fix
|
||||
|
||||
#### Scenario: Checking shared and local state drift
|
||||
- **WHEN** shared workspace state and machine-local path state do not agree
|
||||
- **THEN** doctor SHALL explain which link names are affected
|
||||
- **AND** it SHALL distinguish shared workspace links from local-only paths
|
||||
|
||||
#### Scenario: Reporting invalid local state
|
||||
- **WHEN** list or doctor reads a workspace whose machine-local state file cannot be parsed or validated
|
||||
- **THEN** OpenSpec SHALL report status code `workspace_local_state_invalid`
|
||||
- **AND** it SHALL avoid treating the invalid local state as an empty path map for mutation or repair suggestions
|
||||
- **AND** it SHALL not rewrite workspace registry state or machine-local path state
|
||||
|
||||
#### Scenario: Reporting without auto-repair
|
||||
- **WHEN** doctor finds issues
|
||||
- **THEN** it SHALL report all issues it can find
|
||||
- **AND** it SHALL not automatically repair workspace state
|
||||
|
||||
#### Scenario: Using readable human output
|
||||
- **WHEN** doctor prints human output
|
||||
- **THEN** it SHALL show a readable workspace summary, linked repos or folders, and issues when present
|
||||
- **AND** it SHALL avoid printing raw JSON or relying on a rigid YAML dump as the default human experience
|
||||
|
||||
### Requirement: Scriptable Workspace Setup Commands
|
||||
OpenSpec SHALL provide JSON output for direct workspace setup commands.
|
||||
|
||||
#### Scenario: Requesting JSON output
|
||||
- **WHEN** a user passes `--json` to direct workspace setup commands
|
||||
- **THEN** OpenSpec SHALL print machine-readable output
|
||||
- **AND** the output SHALL avoid extra human-readable text
|
||||
- **AND** the output SHALL separate primary objects from structured `status` entries
|
||||
|
||||
#### Scenario: Setup JSON requires non-interactive setup
|
||||
- **WHEN** a user runs `openspec workspace setup --json` without `--no-interactive`
|
||||
- **THEN** OpenSpec SHALL fail clearly
|
||||
- **AND** it SHALL explain that `workspace setup --json` requires `--no-interactive`
|
||||
|
||||
#### Scenario: JSON output disables prompts
|
||||
- **WHEN** a direct workspace setup command runs with `--json`
|
||||
- **THEN** OpenSpec SHALL avoid interactive prompts
|
||||
- **AND** it SHALL fail with structured status output when required choices are ambiguous
|
||||
|
||||
#### Scenario: JSON status entry shape
|
||||
- **WHEN** a direct workspace setup command reports warnings, errors, or suggested fixes in JSON output
|
||||
- **THEN** each status entry SHALL include a stable `code`, a `severity`, and a human-readable `message`
|
||||
- **AND** status entries MAY include `target` and `fix` fields when a specific object field or suggested command is useful
|
||||
|
||||
#### Scenario: JSON object status shape
|
||||
- **WHEN** a direct workspace setup command emits JSON for workspace, link, or list objects
|
||||
- **THEN** each object MAY include a `status` array for object-specific warnings or errors
|
||||
- **AND** the top-level response SHALL include a `status` array for command-level warnings or errors
|
||||
- **AND** healthy objects and healthy responses SHALL use an empty `status` array
|
||||
|
||||
#### Scenario: Commands with JSON output
|
||||
- **WHEN** users run `workspace setup --no-interactive`, `workspace list`, `workspace link`, `workspace relink`, or `workspace doctor`
|
||||
- **THEN** each command SHALL support JSON output
|
||||
@@ -0,0 +1,121 @@
|
||||
## 1. POC Findings And Scope
|
||||
|
||||
- [x] 1.1 Confirm `setup`, `list`, and `doctor` belong to this slice
|
||||
- [x] 1.2 Capture that setup should not own preferred agent or workspace open behavior
|
||||
- [x] 1.3 Capture that linked repos or folders and monorepo paths are allowed without repo-local OpenSpec state
|
||||
- [x] 1.4 Capture decisions for JSON output, `ls`, `.gitignore`, non-interactive setup, required first link, and relink behavior
|
||||
- [x] 1.5 Capture that public `workspace create` is out of scope for the first release
|
||||
- [x] 1.6 Capture `link`/`relink` as the user-facing commands
|
||||
|
||||
## 2. Workspace Setup
|
||||
|
||||
- [x] 2.1 Implement `openspec workspace setup` as the only public creation path
|
||||
- [x] 2.2 Prompt for workspace name first in interactive setup
|
||||
- [x] 2.3 Validate workspace names as kebab-case and let interactive users retry invalid names
|
||||
- [x] 2.4 Require at least one existing repo or folder path during setup
|
||||
- [x] 2.5 Infer link names from folder basenames during setup
|
||||
- [x] 2.6 Let users add more repos or folders with a simple repeated prompt
|
||||
- [x] 2.7 Run `workspace doctor` after setup and show a readable summary
|
||||
- [x] 2.8 Print the workspace location, planning path, linked repos or folders, and next useful commands
|
||||
- [x] 2.9 Keep preferred agent prompts and workspace opening out of this slice
|
||||
- [x] 2.10 Add `.gitignore` handling for machine-local workspace state
|
||||
- [x] 2.11 Record created workspaces in the local workspace registry
|
||||
- [x] 2.12 Add tests for native Windows/PowerShell and WSL2-compatible path construction where practical
|
||||
|
||||
## 3. Non-Interactive Setup
|
||||
|
||||
- [x] 3.1 Add `workspace setup --no-interactive --name <name> --link <path>` support
|
||||
- [x] 3.2 Support repeated `--link` values
|
||||
- [x] 3.3 Support `--link <path>` with inferred names
|
||||
- [x] 3.4 Support `--link <name>=<path>` with explicit names
|
||||
- [x] 3.5 Fail cleanly when non-interactive setup is missing a name or at least one link
|
||||
- [x] 3.6 Resolve relative link paths to verified absolute runtime-local paths before storing local state
|
||||
- [x] 3.7 Require `--no-interactive` when `workspace setup --json` is used
|
||||
- [x] 3.8 Add `--json` output for non-interactive setup
|
||||
- [x] 3.9 Preserve the interactive setup UX when `--no-interactive` is not passed
|
||||
|
||||
## 4. Workspace Listing
|
||||
|
||||
- [x] 4.1 Implement `openspec workspace list`
|
||||
- [x] 4.2 Add `workspace ls` as an alias for `workspace list`
|
||||
- [x] 4.3 List known OpenSpec-managed workspaces from the local workspace registry
|
||||
- [x] 4.4 Handle the no-workspaces case with a clear next step
|
||||
- [x] 4.5 Show each workspace location and linked repos or folders
|
||||
- [x] 4.6 Report stale registry entries with status entries without deleting, rewriting, or repairing registry state
|
||||
- [x] 4.7 Add JSON output with typed workspace objects and structured status arrays
|
||||
|
||||
## 5. Workspace Selection
|
||||
|
||||
- [x] 5.1 Make workspace commands work from outside workspace directories
|
||||
- [x] 5.2 Add `--workspace <name>` to commands that need one workspace
|
||||
- [x] 5.3 Use the current workspace when running from inside a workspace
|
||||
- [x] 5.4 Use unregistered current workspaces with a non-fatal warning status
|
||||
- [x] 5.5 Record unregistered current workspaces in the local registry after successful `workspace link` or `workspace relink`
|
||||
- [x] 5.6 Keep `workspace doctor` diagnostic-only when the current workspace is unregistered
|
||||
- [x] 5.7 Show an interactive picker when multiple known workspaces exist and no workspace is specified
|
||||
- [x] 5.8 Select the only known workspace automatically when there is exactly one
|
||||
- [x] 5.9 Fail clearly in non-interactive mode when workspace selection is ambiguous
|
||||
- [x] 5.10 Fail with structured status output instead of prompting when `--json` workspace selection is ambiguous
|
||||
- [x] 5.11 Use the local workspace registry for workspace lookup
|
||||
|
||||
## 6. Workspace Links
|
||||
|
||||
- [x] 6.1 Implement `openspec workspace link <path>` with inferred link names
|
||||
- [x] 6.2 Implement `openspec workspace link <name> <path>` with explicit link names
|
||||
- [x] 6.3 Accept full repo roots and monorepo package/service/app folder paths
|
||||
- [x] 6.4 Require linked paths to exist
|
||||
- [x] 6.5 Allow links without repo-local `openspec/`
|
||||
- [x] 6.6 Store stable link names in shared state and local paths in machine-local state
|
||||
- [x] 6.7 Keep link names folder-style, and detect duplicate link names with a specific error that shows the existing link path and suggests a different name or `workspace relink`
|
||||
- [x] 6.8 Resolve relative linked paths to verified absolute runtime-local paths before storing local state
|
||||
- [x] 6.9 Preserve native Windows and WSL2-style paths as local path values without cross-runtime translation
|
||||
- [x] 6.10 Ensure link only records state and does not edit the linked repo/folder
|
||||
- [x] 6.11 Add `--json` output for `workspace link`
|
||||
|
||||
## 7. Workspace Relinks
|
||||
|
||||
- [x] 7.1 Implement `openspec workspace relink <name> <path>`
|
||||
- [x] 7.2 Let users repair or change the local path for an existing link
|
||||
- [x] 7.3 Require relink paths to exist
|
||||
- [x] 7.4 Resolve relative relink paths to verified absolute runtime-local paths before storing local state
|
||||
- [x] 7.5 Keep owner or handoff metadata out of this slice
|
||||
- [x] 7.6 Add `--json` output for `workspace relink`
|
||||
- [x] 7.7 Return a clear error for unknown link names
|
||||
|
||||
## 8. Workspace Doctor
|
||||
|
||||
- [x] 8.1 Implement `openspec workspace doctor` for one selected workspace only
|
||||
- [x] 8.2 Show the workspace location and workspace planning path
|
||||
- [x] 8.3 Show linked repos or folders in readable human output with a clear issues section
|
||||
- [x] 8.4 Report missing local paths, missing filesystem paths, local-only names, and selected-workspace location problems
|
||||
- [x] 8.5 Report `repo_specs_path` when repo-local `openspec/specs` exists and `null` otherwise
|
||||
- [x] 8.6 Include suggested fixes for each issue
|
||||
- [x] 8.7 Avoid automatic repair behavior
|
||||
- [x] 8.8 Add JSON output with typed workspace/link objects and structured status arrays
|
||||
- [x] 8.9 Keep stale registry cleanup commands such as `workspace forget` out of this slice
|
||||
|
||||
## 9. Documentation And Guidance
|
||||
|
||||
- [x] 9.1 Document setup/list/link/relink/doctor in user-facing product language
|
||||
- [x] 9.2 Document linked repos or folders and large-monorepo folder links
|
||||
- [x] 9.3 Document that workspace visibility is not change commitment
|
||||
- [x] 9.4 Avoid "working set", "code area", "entry", "alias", and "local overlay" in human-facing docs
|
||||
- [x] 9.5 Document JSON output support and the object/status response pattern for non-interactive/direct commands
|
||||
- [x] 9.6 Document global command behavior, workspace picker behavior, and `--workspace <name>`
|
||||
- [x] 9.7 Document that setup controls workspace storage and always shows the workspace location
|
||||
|
||||
## 10. Verification
|
||||
|
||||
- [x] 10.1 Run `openspec validate workspace-create-and-register-repos --strict`
|
||||
- [x] 10.2 Run targeted command tests for workspace setup/list/link/relink/doctor, including doctor inferring the current workspace
|
||||
- [x] 10.3 Run targeted tests for links without repo-local OpenSpec and monorepo folder links
|
||||
- [x] 10.4 Run targeted tests for JSON output, `ls`, `.gitignore`, non-interactive setup, required first link, verified absolute path storage, and JSON/no-interactive prompt suppression
|
||||
- [x] 10.5 Run targeted tests for global command selection, unregistered current workspace handling, and local workspace registry behavior
|
||||
|
||||
## 11. Review Fixes
|
||||
|
||||
- [x] 11.1 Preserve `=` characters in inferred setup link paths while keeping explicit `--link <name>=<path>` support
|
||||
- [x] 11.2 Add reusable core helpers for optional local state reads and setup link input parsing
|
||||
- [x] 11.3 Fail `workspace link` and `workspace relink` before mutation when local state is invalid
|
||||
- [x] 11.4 Report invalid local state distinctly in `workspace list` and `workspace doctor`
|
||||
- [x] 11.5 Add regression tests for equals-sign setup paths and malformed local state behavior
|
||||
@@ -0,0 +1,266 @@
|
||||
## Product Shape
|
||||
|
||||
`workspace open` should feel like opening a multi-root working set.
|
||||
|
||||
The user model is:
|
||||
|
||||
```text
|
||||
workspace setup = create the planning home and choose the default opener
|
||||
workspace links = the repos or folders OpenSpec can plan across
|
||||
workspace open = open that linked working set
|
||||
--agent = use a different agent for this one session
|
||||
--editor = open the working set as an editor workspace
|
||||
```
|
||||
|
||||
Repo or folder visibility supports exploration and planning. Opening a workspace gives the agent or editor access to linked paths, and implementation starts through an explicit later workflow.
|
||||
|
||||
## Command Surface
|
||||
|
||||
Supported v1 forms:
|
||||
|
||||
```bash
|
||||
openspec workspace open
|
||||
openspec workspace open platform
|
||||
openspec workspace open --agent codex
|
||||
openspec workspace open platform --agent github-copilot
|
||||
openspec workspace open --editor
|
||||
```
|
||||
|
||||
The positional workspace name is the primary explicit selection surface for `open`. User-facing docs should prefer the positional form because a flag such as `--workspace <name>` repeats the noun.
|
||||
|
||||
For consistency with other workspace commands and scripts, `workspace open` may also support `--workspace <name>` as an alias for the positional name:
|
||||
|
||||
```bash
|
||||
openspec workspace open platform
|
||||
openspec workspace open --workspace platform
|
||||
```
|
||||
|
||||
User-facing docs should prefer the positional form. If both are provided and they differ, OpenSpec should fail with a clear conflict error.
|
||||
|
||||
`--prepare-only` should not be included. The POC used it to build and print launch surfaces without starting the external tool, but that does not map cleanly to a user-facing intent.
|
||||
|
||||
`--json` should not be included in this slice. If a future integration needs a machine-readable resolved-open context, design that as a separate context/query surface instead of overloading the launching command.
|
||||
|
||||
`--change` should be deferred. Change-scoped open depends on workspace change planning and target semantics that this slice should not invent.
|
||||
|
||||
## Workspace Selection
|
||||
|
||||
Selection should follow this order:
|
||||
|
||||
1. If a positional workspace name is provided, open that known workspace.
|
||||
2. Otherwise, if the command runs from inside a workspace, open the current workspace.
|
||||
3. Otherwise, if exactly one workspace is known locally, open it.
|
||||
4. Otherwise, if multiple workspaces are known and the terminal is interactive, present a picker.
|
||||
5. Otherwise, fail with a clear message that names the known workspaces and asks the user to pass the workspace name.
|
||||
|
||||
This keeps the common cases direct while still supporting global use.
|
||||
|
||||
## Preferred Opener
|
||||
|
||||
Workspace setup should ask which opener the user wants by default. The answer is machine-local state because different machines may have different installed agents or editors.
|
||||
|
||||
`workspace open` uses the saved opener when no override is passed.
|
||||
|
||||
`--agent <tool>` is a one-session override that leaves the saved preference unchanged. Persisting a changed default should require an explicit preference/config action in a later slice if users need it.
|
||||
|
||||
This slice should not add global workspace opener config. OpenSpec already has a global config system, and workspace-level defaults can be added there later if repeated setup makes the local prompt feel noisy.
|
||||
|
||||
The local preference should be shaped so a future global default can fit underneath it with smooth migration. The intended precedence is:
|
||||
|
||||
```text
|
||||
command override
|
||||
-> workspace-local preferred opener
|
||||
-> future global workspace default opener
|
||||
-> interactive prompt or built-in fallback
|
||||
```
|
||||
|
||||
In future config terms, that global default might look like `workspace.defaultOpener`; this slice documents the precedence for later implementation.
|
||||
|
||||
Store the preferred opener as a structured object in `.openspec-workspace/local.yaml`:
|
||||
|
||||
```yaml
|
||||
preferred_opener:
|
||||
kind: agent
|
||||
id: codex
|
||||
```
|
||||
|
||||
```yaml
|
||||
preferred_opener:
|
||||
kind: editor
|
||||
id: vscode
|
||||
```
|
||||
|
||||
Allowed initial values:
|
||||
|
||||
```text
|
||||
kind: agent, id: codex
|
||||
kind: agent, id: claude
|
||||
kind: agent, id: github-copilot
|
||||
kind: editor, id: vscode
|
||||
```
|
||||
|
||||
The structure keeps the agent/editor distinction clear and leaves room for future opener variants without changing the local-state shape.
|
||||
|
||||
Interactive setup should show all supported opener choices, but it should order detected/available openers first. Unavailable choices should still be visible with a note such as `not found on PATH`.
|
||||
|
||||
Setup should prefer the plain editor option over an agent when a fallback default is needed for an interactive picker.
|
||||
|
||||
Non-interactive setup stores a preferred opener when the caller explicitly passes an opener option. Otherwise, it leaves opener selection for a later interactive `workspace open` prompt or a non-interactive error that explains how to choose an opener.
|
||||
|
||||
The setup-time flag should be:
|
||||
|
||||
```bash
|
||||
openspec workspace setup --no-interactive --name platform --link /repo --opener codex
|
||||
openspec workspace setup --no-interactive --name platform --link /repo --opener editor
|
||||
```
|
||||
|
||||
`--opener <id>` sets the stored preference. It is different from `workspace open --agent <id>` and `workspace open --editor`, which are one-session runtime overrides.
|
||||
|
||||
Initial opener detection should stay simple and executable-based:
|
||||
|
||||
```text
|
||||
VS Code editor: code
|
||||
Codex: codex
|
||||
Claude: claude
|
||||
GitHub Copilot in VS Code: code
|
||||
```
|
||||
|
||||
Keep initial detection scoped to executable availability in this slice.
|
||||
|
||||
Supported agent values for the initial open surface should be limited to tools with a real launch or attachment mechanism:
|
||||
|
||||
```text
|
||||
claude
|
||||
codex
|
||||
github-copilot
|
||||
```
|
||||
|
||||
Plain editor open should be represented by `--editor` with an explicit editor kind.
|
||||
|
||||
For this slice, `--editor` means VS Code editor. The `.code-workspace` format is VS Code-specific, so prompts and errors should call this `VS Code editor` rather than implying generic editor support.
|
||||
|
||||
`github-copilot` means the VS Code Copilot experience. It should open the maintained `.code-workspace` in VS Code because that is the product surface where this Copilot mode is available.
|
||||
|
||||
If OpenSpec later supports a Copilot CLI agent, it should use a distinct value such as `github-copilot-cli` and launch the CLI agent directly. VS Code Copilot and a CLI agent have different opener mechanics, so they should remain distinct opener values.
|
||||
|
||||
## Opener Availability
|
||||
|
||||
`workspace open` should fail with a clear error when the selected opener is unavailable on the current machine.
|
||||
|
||||
The selected opener remains required because it represents user intent, whether it came from local preference or a command-line override.
|
||||
|
||||
Errors should name the missing executable or unavailable opener and suggest a concrete next step. For editor-based open, the error should include the `.code-workspace` path so the user can open it manually if needed.
|
||||
|
||||
When no preferred opener is stored and no command-line override is provided, `workspace open` should prompt in interactive mode. In non-interactive mode, it should fail and tell the user to pass either an agent override or the editor option.
|
||||
|
||||
## Editor Open
|
||||
|
||||
`--editor` opens the workspace root plus every linked repo or folder with a valid local path.
|
||||
|
||||
For VS Code-style editor support, OpenSpec should create and maintain a `.code-workspace` file as part of the workspace setup/link/relink lifecycle. `workspace open` should launch against existing workspace state.
|
||||
|
||||
Expected local workspace shape:
|
||||
|
||||
```text
|
||||
workspace-root/
|
||||
changes/
|
||||
<workspace-name>.code-workspace
|
||||
.openspec-workspace/
|
||||
workspace.yaml
|
||||
local.yaml
|
||||
```
|
||||
|
||||
The `.code-workspace` file should include the workspace root and each linked repo or folder with a valid local path. Because linked paths come from machine-local workspace state, OpenSpec-created workspaces should ignore the maintained `.code-workspace` file by default.
|
||||
|
||||
The ignore rule should target the specific maintained file and leave other `*.code-workspace` files available for user-authored tracking:
|
||||
|
||||
```text
|
||||
<workspace-name>.code-workspace
|
||||
```
|
||||
|
||||
This lets teams add a separate user-authored portable `.code-workspace` later if they have a shared relative-path layout.
|
||||
|
||||
`workspace setup`, `workspace link`, and `workspace relink` should all run the same open-surface sync after mutating workspace state. That sync owns:
|
||||
|
||||
- `AGENTS.md`
|
||||
- `<workspace-name>.code-workspace`
|
||||
- workspace ignore rules for machine-local files
|
||||
|
||||
Even when a command only changes local state, such as `workspace relink`, it should refresh the full openable workspace surface so user-facing files do not drift.
|
||||
|
||||
`--agent github-copilot` may use the same editor workspace mechanics, but it also needs Copilot prompt context. Plain `--editor` keeps a normal editor-workspace intent.
|
||||
|
||||
`--agent github-copilot` should still open VS Code. The distinction from `--editor` is intent: `--editor` opens the workspace as a normal editor workspace, while `--agent github-copilot` opens the same editor workspace for the user to work with the VS Code Copilot agent experience.
|
||||
|
||||
## Workspace Guidance
|
||||
|
||||
Workspace setup should install stable guidance in the workspace root, preferably `AGENTS.md`.
|
||||
|
||||
The guidance should explain durable workspace rules:
|
||||
|
||||
- the workspace root is the planning home
|
||||
- `changes/` contains workspace-level planning
|
||||
- linked repos and folders are available for exploration and planning
|
||||
- visibility supports exploration and planning
|
||||
- implementation edits start after the user explicitly asks for implementation work
|
||||
|
||||
The managed `AGENTS.md` text should stay short and durable, covering stable workspace guidance while runtime details remain discoverable from workspace state. A starting shape:
|
||||
|
||||
```markdown
|
||||
# OpenSpec Workspace Guidance
|
||||
|
||||
This directory is an OpenSpec workspace for planning across linked repos or folders.
|
||||
|
||||
- Use `changes/` for workspace-level planning.
|
||||
- Linked repos and folders are available for exploration and planning.
|
||||
- Repo or folder visibility supports exploration and planning.
|
||||
- Make implementation edits after the user explicitly asks for implementation work.
|
||||
- Treat linked repos and folders as the implementation homes for their owned code.
|
||||
- Use OpenSpec workspace commands instead of hand-editing `.openspec-workspace/*.yaml`.
|
||||
```
|
||||
|
||||
`workspace open` is a launching feature. It should launch the selected opener against existing workspace files.
|
||||
|
||||
For Claude and Codex, `workspace open` may still need to pass workspace and linked directory arguments to the agent process at launch because those tools do not consume `.code-workspace` directly. If an opener requires an initial prompt argument, it should be minimal, such as `Open this OpenSpec workspace.`
|
||||
|
||||
Dynamic workspace facts should normally be discoverable from existing files:
|
||||
|
||||
- linked paths: `.openspec-workspace/local.yaml`
|
||||
- stable link names: `.openspec-workspace/workspace.yaml`
|
||||
- active workspace changes: `changes/`
|
||||
- editor working set: `<workspace-name>.code-workspace`
|
||||
|
||||
Report a command file or prompt file path only when the file is actually written and used.
|
||||
|
||||
OpenSpec should own a marked workspace-guidance block inside `AGENTS.md`:
|
||||
|
||||
```markdown
|
||||
<!-- OPENSPEC:WORKSPACE-GUIDANCE:START -->
|
||||
# OpenSpec Workspace Guidance
|
||||
|
||||
...
|
||||
<!-- OPENSPEC:WORKSPACE-GUIDANCE:END -->
|
||||
```
|
||||
|
||||
`workspace setup`, `workspace link`, and `workspace relink` may rewrite that marked block during open-surface sync. Content outside the marked block should be preserved so users can keep their own workspace notes in the same file.
|
||||
|
||||
If `AGENTS.md` is missing, OpenSpec should recreate it. If `AGENTS.md` exists and the markers are absent, OpenSpec should append the managed block while preserving existing content.
|
||||
|
||||
## Linked Paths
|
||||
|
||||
Root workspace open should attach every linked repo or folder with a valid local path.
|
||||
|
||||
Broken links are skipped during workspace open. OpenSpec should surface clear status in human output, with `openspec workspace doctor` as the repair path.
|
||||
|
||||
Links with repo-local `openspec/` state absent remain valid for workspace open. Missing repo-local OpenSpec state can matter later for implementation readiness while still allowing visibility for exploration and planning.
|
||||
|
||||
## Safety Boundary
|
||||
|
||||
The opening prompt or editor guidance should say:
|
||||
|
||||
```text
|
||||
Linked repos and folders are visible for exploration and planning.
|
||||
Make implementation edits after the user explicitly asks for implementation work.
|
||||
```
|
||||
|
||||
Prompt guidance is acceptable for this slice because apply/verify/archive sit outside the open surface. Later implementation workflows should enforce mode and scope through explicit context providers as well as prompt wording.
|
||||
@@ -0,0 +1,65 @@
|
||||
## Why
|
||||
|
||||
After a user creates a workspace and links repos or folders, they need to open that workspace with their preferred agent or editor and have the working set available immediately.
|
||||
|
||||
The workspace should provide repo and folder locations, link names, and the context that distinguishes planning from implementation.
|
||||
|
||||
## What Changes
|
||||
|
||||
Add the workspace-open experience:
|
||||
|
||||
```text
|
||||
Open this workspace.
|
||||
Use my preferred opener by default and honor explicit opener overrides.
|
||||
The opener sees the workspace location, linked repos or folders, current changes, and relevant instructions.
|
||||
```
|
||||
|
||||
Links are the planning context. The local registry serves as a workspace-discovery index for finding known workspaces on the current machine.
|
||||
|
||||
Expected user surface:
|
||||
|
||||
```bash
|
||||
openspec workspace open
|
||||
openspec workspace open platform
|
||||
openspec workspace open --agent codex
|
||||
openspec workspace open platform --agent github-copilot
|
||||
openspec workspace open --editor
|
||||
```
|
||||
|
||||
`workspace open` should open the current workspace when run from inside one, auto-select the only known workspace when run outside a workspace, and present an interactive picker when multiple known workspaces are available. Users can pass a workspace name as the positional argument when they want to choose explicitly.
|
||||
|
||||
Workspace setup should ask for and store a preferred opener in machine-local workspace state. `workspace open` uses that preference by default. `--agent <tool>` is a one-session override that leaves the saved preference unchanged.
|
||||
|
||||
`--editor` opens the workspace as an editor workspace. This is related to, but distinct from, `--agent github-copilot`: GitHub Copilot needs editor workspace support plus agent prompt context, while plain editor open should focus on opening the linked working set.
|
||||
|
||||
Workspace guidance should live in durable workspace files where possible:
|
||||
|
||||
- stable behavior belongs in workspace-level `AGENTS.md`
|
||||
- opener-specific launch prompts stay minimal when required
|
||||
- linked repos or folders are visible for exploration and planning before a change exists
|
||||
|
||||
This slice supports root workspace launching through the documented opener forms. Public preview (`--prepare-only`) and machine-readable context (`--json`) surfaces belong in a future context/query design if a clear user need appears.
|
||||
|
||||
This slice focuses on root workspace open behavior. Change-scoped sessions need the target model from workspace change planning before they can be specified cleanly.
|
||||
|
||||
Planning dependency:
|
||||
|
||||
- Depends on `workspace-create-and-register-repos`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `workspace-open`: Opens a workspace through a preferred agent or VS Code editor with linked repos or folders available for exploration and planning.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `workspace-foundation`: Extends machine-local workspace state and setup/link/relink behavior with a preferred opener and maintained openable workspace surface.
|
||||
|
||||
## Impact
|
||||
|
||||
- `openspec workspace open`
|
||||
- Workspace setup preferred opener prompt and local preference storage.
|
||||
- Workspace prompt, editor workspace, and agent-launch context.
|
||||
- Generated or committed agent guidance for workspace mode.
|
||||
- Tests for opening inside a workspace, auto-selecting one known workspace, picking among multiple known workspaces, opening by workspace name, one-session agent overrides, and editor open.
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace Preferred Opener State
|
||||
OpenSpec SHALL store a workspace's preferred opener in machine-local workspace state when the user explicitly chooses one.
|
||||
|
||||
#### Scenario: Recording an interactive setup opener choice
|
||||
- **WHEN** an interactive user chooses a preferred opener during `openspec workspace setup`
|
||||
- **THEN** OpenSpec SHALL record the opener in `.openspec-workspace/local.yaml`
|
||||
- **AND** the stored value SHALL use a structured `preferred_opener` object with `kind` and `id`
|
||||
|
||||
#### Scenario: Recording a non-interactive setup opener choice
|
||||
- **WHEN** a non-interactive user runs `openspec workspace setup --no-interactive --opener codex`
|
||||
- **THEN** OpenSpec SHALL record `preferred_opener.kind` as `agent`
|
||||
- **AND** it SHALL record `preferred_opener.id` as `codex`
|
||||
|
||||
#### Scenario: Leaving opener unset during non-interactive setup
|
||||
- **WHEN** a non-interactive user runs `openspec workspace setup --no-interactive` with opener selection omitted
|
||||
- **THEN** OpenSpec SHALL leave the workspace preferred opener unset
|
||||
- **AND** the unset state SHALL allow `workspace open` to prompt later
|
||||
|
||||
#### Scenario: Supported preferred opener values
|
||||
- **WHEN** OpenSpec accepts a preferred opener value
|
||||
- **THEN** it SHALL accept `codex`, `claude`, `github-copilot`, and `editor`
|
||||
- **AND** it SHALL map `editor` to `kind: editor` and `id: vscode`
|
||||
- **AND** it SHALL map agent values to `kind: agent` and the matching agent `id`
|
||||
|
||||
#### Scenario: Ordering setup opener choices
|
||||
- **WHEN** interactive setup displays opener choices
|
||||
- **THEN** OpenSpec SHALL show all supported openers
|
||||
- **AND** it SHALL order openers with detected executables before unavailable openers
|
||||
- **AND** unavailable openers SHALL remain visible with an availability note
|
||||
|
||||
### Requirement: Maintained Workspace Open Surface
|
||||
OpenSpec SHALL maintain files that make a workspace directly openable after setup and link changes.
|
||||
|
||||
#### Scenario: Creating the open surface during setup
|
||||
- **WHEN** `openspec workspace setup` creates a workspace
|
||||
- **THEN** OpenSpec SHALL create or refresh `AGENTS.md`
|
||||
- **AND** it SHALL create or refresh `<workspace-name>.code-workspace`
|
||||
- **AND** it SHALL create or refresh workspace ignore rules for machine-local open files
|
||||
|
||||
#### Scenario: Refreshing the open surface after linking
|
||||
- **WHEN** `openspec workspace link` succeeds
|
||||
- **THEN** OpenSpec SHALL refresh `AGENTS.md`
|
||||
- **AND** it SHALL refresh `<workspace-name>.code-workspace`
|
||||
- **AND** it SHALL refresh workspace ignore rules for machine-local open files
|
||||
|
||||
#### Scenario: Refreshing the open surface after relinking
|
||||
- **WHEN** `openspec workspace relink` succeeds
|
||||
- **THEN** OpenSpec SHALL refresh `AGENTS.md`
|
||||
- **AND** it SHALL refresh `<workspace-name>.code-workspace`
|
||||
- **AND** it SHALL refresh workspace ignore rules for machine-local open files
|
||||
|
||||
#### Scenario: Building the VS Code workspace file
|
||||
- **WHEN** OpenSpec refreshes `<workspace-name>.code-workspace`
|
||||
- **THEN** the file SHALL include the workspace root
|
||||
- **AND** the workspace root folder entry SHALL use the root path without a synthetic display name
|
||||
- **AND** it SHALL include every linked repo or folder with a valid local path
|
||||
- **AND** it SHALL omit linked repos or folders whose local paths are missing or invalid
|
||||
|
||||
#### Scenario: Ignoring the maintained VS Code workspace file
|
||||
- **WHEN** OpenSpec refreshes workspace ignore rules
|
||||
- **THEN** it SHALL ignore the specific maintained `<workspace-name>.code-workspace` file
|
||||
- **AND** user-authored `*.code-workspace` files SHALL remain eligible for tracking
|
||||
|
||||
#### Scenario: Preserving user-authored AGENTS content
|
||||
- **GIVEN** `AGENTS.md` contains content outside the OpenSpec workspace guidance markers
|
||||
- **WHEN** OpenSpec refreshes workspace guidance
|
||||
- **THEN** it SHALL replace only the marked OpenSpec workspace guidance block
|
||||
- **AND** it SHALL preserve content outside the markers
|
||||
|
||||
#### Scenario: Appending AGENTS guidance when markers are missing
|
||||
- **GIVEN** `AGENTS.md` exists and OpenSpec workspace guidance markers are absent
|
||||
- **WHEN** OpenSpec refreshes workspace guidance
|
||||
- **THEN** it SHALL append the marked OpenSpec workspace guidance block
|
||||
- **AND** it SHALL preserve the existing file content
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace Open Command
|
||||
OpenSpec SHALL provide a `workspace open` command that opens an OpenSpec workspace working set through an agent or VS Code editor.
|
||||
|
||||
#### Scenario: Opening the current workspace
|
||||
- **GIVEN** the command runs from inside an OpenSpec workspace
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL open that current workspace
|
||||
- **AND** it SHALL use the selected opener for that workspace
|
||||
|
||||
#### Scenario: Opening a named workspace
|
||||
- **GIVEN** a workspace named `platform` is known locally
|
||||
- **WHEN** the user runs `openspec workspace open platform`
|
||||
- **THEN** OpenSpec SHALL open the `platform` workspace
|
||||
|
||||
#### Scenario: Opening a named workspace with the selection flag
|
||||
- **GIVEN** a workspace named `platform` is known locally
|
||||
- **WHEN** the user runs `openspec workspace open --workspace platform`
|
||||
- **THEN** OpenSpec SHALL open the `platform` workspace
|
||||
|
||||
#### Scenario: Conflicting workspace selectors
|
||||
- **GIVEN** workspaces named `platform` and `checkout` are known locally
|
||||
- **WHEN** the user runs `openspec workspace open platform --workspace checkout`
|
||||
- **THEN** OpenSpec SHALL fail with a clear conflict error
|
||||
- **AND** the error SHALL name both conflicting selectors
|
||||
|
||||
#### Scenario: Handling unsupported preview and JSON flags
|
||||
- **WHEN** the user runs `openspec workspace open` with `--prepare-only` or `--json`
|
||||
- **THEN** OpenSpec SHALL fail with a clear error that the root workspace open surface supports launching through a selected opener
|
||||
- **AND** the error SHALL direct preview or machine-readable context needs to a future context/query surface
|
||||
|
||||
#### Scenario: Handling change-scoped open before workspace planning
|
||||
- **WHEN** the user runs `openspec workspace open --change <id>`
|
||||
- **THEN** OpenSpec SHALL fail with a clear error that this slice supports root workspace open
|
||||
- **AND** the error SHALL direct change-scoped open behavior to future workspace change planning
|
||||
|
||||
### Requirement: Workspace Selection For Open
|
||||
OpenSpec SHALL resolve the workspace to open using current workspace context, local registry state, and interactive selection.
|
||||
|
||||
#### Scenario: Current workspace wins
|
||||
- **GIVEN** the command runs from a workspace folder or one of its subdirectories
|
||||
- **AND** no workspace name is provided
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL open the current workspace
|
||||
|
||||
#### Scenario: Auto-selecting the only known workspace
|
||||
- **GIVEN** the command runs outside a workspace
|
||||
- **AND** exactly one workspace is known locally
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL open that known workspace directly
|
||||
|
||||
#### Scenario: Picking from multiple workspaces
|
||||
- **GIVEN** the command runs outside a workspace
|
||||
- **AND** multiple workspaces are known locally
|
||||
- **AND** the terminal is interactive
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL present a picker with workspace names and locations
|
||||
- **AND** it SHALL open the workspace the user selects
|
||||
|
||||
#### Scenario: Non-interactive ambiguous selection
|
||||
- **GIVEN** the command runs outside a workspace
|
||||
- **AND** multiple workspaces are known locally
|
||||
- **AND** the terminal is non-interactive
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL fail with a clear message listing the known workspace names
|
||||
- **AND** it SHALL ask the user to pass a workspace name
|
||||
|
||||
#### Scenario: No known workspace
|
||||
- **GIVEN** the command runs outside a workspace
|
||||
- **AND** no workspaces are known locally
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL fail with a clear message
|
||||
- **AND** it SHALL suggest running `openspec workspace setup`
|
||||
|
||||
### Requirement: Opener Resolution
|
||||
OpenSpec SHALL resolve the opener from command overrides, workspace-local preference, or an interactive prompt.
|
||||
|
||||
#### Scenario: Conflicting opener overrides
|
||||
- **WHEN** the user runs `openspec workspace open --agent codex --editor`
|
||||
- **THEN** OpenSpec SHALL fail with a clear conflict error naming `--agent` and `--editor`
|
||||
- **AND** it SHALL avoid launching any opener
|
||||
- **AND** it SHALL leave the stored preferred opener unchanged
|
||||
|
||||
#### Scenario: Using the stored preferred opener
|
||||
- **GIVEN** the workspace has a machine-local preferred opener
|
||||
- **WHEN** the user runs `openspec workspace open` using default opener resolution
|
||||
- **THEN** OpenSpec SHALL use the stored preferred opener
|
||||
|
||||
#### Scenario: Overriding with an agent for one session
|
||||
- **GIVEN** the workspace has a stored preferred opener
|
||||
- **WHEN** the user runs `openspec workspace open --agent codex`
|
||||
- **THEN** OpenSpec SHALL use Codex for that open command
|
||||
- **AND** it SHALL leave the stored preferred opener unchanged
|
||||
|
||||
#### Scenario: Overriding with VS Code editor for one session
|
||||
- **GIVEN** the workspace has a stored preferred opener
|
||||
- **WHEN** the user runs `openspec workspace open --editor`
|
||||
- **THEN** OpenSpec SHALL open the workspace in VS Code editor mode
|
||||
- **AND** it SHALL leave the stored preferred opener unchanged
|
||||
|
||||
#### Scenario: Prompting when no opener is stored
|
||||
- **GIVEN** the workspace has no stored preferred opener
|
||||
- **AND** the terminal is interactive
|
||||
- **WHEN** the user runs `openspec workspace open` using default opener resolution
|
||||
- **THEN** OpenSpec SHALL prompt the user to choose an opener
|
||||
- **AND** it SHALL only offer openers with detected executables
|
||||
|
||||
#### Scenario: Failing when no opener can be prompted
|
||||
- **GIVEN** the workspace has no stored preferred opener
|
||||
- **AND** the terminal is interactive
|
||||
- **AND** no supported opener executable is available on `PATH`
|
||||
- **WHEN** the user runs `openspec workspace open` using default opener resolution
|
||||
- **THEN** OpenSpec SHALL fail with a clear message that no supported opener is available
|
||||
- **AND** it SHALL avoid prompting with unlaunchable choices
|
||||
|
||||
#### Scenario: Failing when no opener is stored in non-interactive mode
|
||||
- **GIVEN** the workspace has no stored preferred opener
|
||||
- **AND** the terminal is non-interactive
|
||||
- **WHEN** the user runs `openspec workspace open` using default opener resolution
|
||||
- **THEN** OpenSpec SHALL fail with a clear message
|
||||
- **AND** it SHALL ask the user to pass `--agent <tool>` or `--editor`
|
||||
|
||||
### Requirement: Opener Launch Behavior
|
||||
OpenSpec SHALL launch the selected opener using existing workspace files and linked path state.
|
||||
|
||||
#### Scenario: Opening VS Code editor
|
||||
- **GIVEN** the user selected the VS Code editor opener
|
||||
- **WHEN** `code` is available on `PATH`
|
||||
- **THEN** OpenSpec SHALL open the workspace's maintained `.code-workspace` file with VS Code
|
||||
|
||||
#### Scenario: Opening GitHub Copilot in VS Code
|
||||
- **GIVEN** the user selected `--agent github-copilot`
|
||||
- **WHEN** `code` is available on `PATH`
|
||||
- **THEN** OpenSpec SHALL open the workspace's maintained `.code-workspace` file with VS Code
|
||||
- **AND** it SHALL treat this as the VS Code Copilot experience
|
||||
|
||||
#### Scenario: Opening Codex
|
||||
- **GIVEN** the user selected `--agent codex`
|
||||
- **WHEN** `codex` is available on `PATH`
|
||||
- **THEN** OpenSpec SHALL launch Codex from the workspace root
|
||||
- **AND** it SHALL attach every linked repo or folder with a valid local path using Codex's supported directory attachment mechanism
|
||||
|
||||
#### Scenario: Opening Claude
|
||||
- **GIVEN** the user selected `--agent claude`
|
||||
- **WHEN** `claude` is available on `PATH`
|
||||
- **THEN** OpenSpec SHALL launch Claude from the workspace root
|
||||
- **AND** it SHALL attach every linked repo or folder with a valid local path using Claude's supported directory attachment mechanism
|
||||
|
||||
#### Scenario: Missing opener executable
|
||||
- **GIVEN** the selected opener requires an executable that is not available on `PATH`
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL fail with a clear error naming the missing executable
|
||||
- **AND** it SHALL keep the selected opener as the required opener
|
||||
|
||||
#### Scenario: Missing VS Code executable
|
||||
- **GIVEN** the selected opener is VS Code editor or GitHub Copilot in VS Code
|
||||
- **AND** `code` is not available on `PATH`
|
||||
- **WHEN** the user runs `openspec workspace open`
|
||||
- **THEN** OpenSpec SHALL fail with a clear error naming `code`
|
||||
- **AND** it SHALL include the maintained `.code-workspace` path so the user can open it manually
|
||||
|
||||
### Requirement: Linked Working Set Visibility
|
||||
OpenSpec SHALL make linked repos and folders visible for workspace exploration and planning before change creation.
|
||||
|
||||
#### Scenario: Attaching valid linked paths
|
||||
- **GIVEN** a workspace has linked repos or folders with valid local paths
|
||||
- **WHEN** the user opens the workspace through an opener that supports linked directory attachment
|
||||
- **THEN** OpenSpec SHALL include every valid linked path in the opened working set
|
||||
- **AND** it SHALL support opening before a workspace change exists
|
||||
|
||||
#### Scenario: Skipping broken linked paths
|
||||
- **GIVEN** a workspace has at least one linked path that is missing or not recorded locally
|
||||
- **WHEN** the user opens the workspace
|
||||
- **THEN** OpenSpec SHALL skip the broken linked path
|
||||
- **AND** it SHALL report that the path was skipped with `openspec workspace doctor` as the repair path
|
||||
- **AND** it SHALL continue opening the workspace when the selected opener itself is available
|
||||
|
||||
#### Scenario: Opening links with repo-local OpenSpec state absent
|
||||
- **GIVEN** a linked repo or folder has a valid local path and repo-local `openspec/` state is absent
|
||||
- **WHEN** the user opens the workspace
|
||||
- **THEN** OpenSpec SHALL include that link when its local path is valid
|
||||
- **AND** it SHALL treat missing repo-local OpenSpec state as an implementation-readiness concern for later workflows while continuing open
|
||||
|
||||
### Requirement: Workspace Open Guidance
|
||||
OpenSpec SHALL use durable workspace guidance as the primary context source for root workspace open.
|
||||
|
||||
#### Scenario: Launching with existing workspace guidance
|
||||
- **GIVEN** the workspace has OpenSpec-managed guidance in `AGENTS.md`
|
||||
- **WHEN** the user opens the workspace
|
||||
- **THEN** OpenSpec SHALL refresh the maintained `.code-workspace` from current linked path state
|
||||
- **AND** it SHALL launch the selected opener against refreshed workspace files
|
||||
- **AND** it SHALL use durable workspace files as the primary workspace-open artifact
|
||||
|
||||
#### Scenario: Minimal required launch prompt
|
||||
- **GIVEN** an opener requires an initial prompt argument
|
||||
- **WHEN** OpenSpec launches that opener
|
||||
- **THEN** OpenSpec SHALL use a minimal prompt such as `Open this OpenSpec workspace.`
|
||||
- **AND** durable workspace rules SHALL remain in workspace files
|
||||
@@ -0,0 +1,89 @@
|
||||
## 1. Preferred Opener State
|
||||
|
||||
- [x] 1.1 Add structured `preferred_opener` support to workspace local state parsing and serialization
|
||||
- [x] 1.2 Support backward-compatible parsing for existing local workspace files while adding `preferred_opener`
|
||||
- [x] 1.3 Validate supported opener values: `codex`, `claude`, `github-copilot`, and `editor`
|
||||
- [x] 1.4 Map `editor` to `kind: editor, id: vscode`
|
||||
- [x] 1.5 Map agent opener values to `kind: agent` with the matching `id`
|
||||
- [x] 1.6 Add simple executable detection for `code`, `codex`, and `claude`
|
||||
- [x] 1.7 Add unit tests for preferred opener parsing, serialization, and invalid opener values
|
||||
|
||||
## 2. Setup Opener Selection
|
||||
|
||||
- [x] 2.1 Add interactive setup prompt for the preferred opener
|
||||
- [x] 2.2 Show all supported opener choices with detected openers ordered first
|
||||
- [x] 2.3 Mark unavailable opener choices with a clear availability note
|
||||
- [x] 2.4 Prefer the plain editor option for setup fallback selection when a fallback is needed
|
||||
- [x] 2.5 Add `workspace setup --opener <id>` for non-interactive setup
|
||||
- [x] 2.6 Store a preferred opener during non-interactive setup when `--opener` is provided
|
||||
- [x] 2.7 Add tests for interactive opener selection and non-interactive `--opener`
|
||||
- [x] 2.8 Add tests that non-interactive setup with omitted `--opener` leaves opener unset
|
||||
|
||||
## 3. Open Surface Sync
|
||||
|
||||
- [x] 3.1 Add a shared open-surface sync helper used by setup, link, and relink
|
||||
- [x] 3.2 Create or refresh root `AGENTS.md` with an OpenSpec-managed workspace guidance block
|
||||
- [x] 3.3 Preserve user-authored `AGENTS.md` content outside the managed block
|
||||
- [x] 3.4 Append the managed block to unmarked existing `AGENTS.md` files
|
||||
- [x] 3.5 Create or refresh `<workspace-name>.code-workspace` at the workspace root
|
||||
- [x] 3.6 Include the workspace root and every linked repo or folder with a valid local path in the `.code-workspace`
|
||||
- [x] 3.7 Omit linked repos or folders with missing or invalid local paths from the `.code-workspace`
|
||||
- [x] 3.8 Refresh `.gitignore` with the specific maintained `<workspace-name>.code-workspace` entry
|
||||
- [x] 3.9 Scope ignore updates to the maintained `<workspace-name>.code-workspace` file
|
||||
- [x] 3.10 Add cross-platform tests for `.code-workspace` path construction and Windows-style paths where practical
|
||||
|
||||
## 4. Workspace Open Selection
|
||||
|
||||
- [x] 4.1 Add `openspec workspace open [name]`
|
||||
- [x] 4.2 Support `openspec workspace open --workspace <name>` as an alias for the positional name
|
||||
- [x] 4.3 Fail clearly when positional name and `--workspace` are both provided with different values
|
||||
- [x] 4.4 Open the current workspace when run from a workspace folder or subdirectory
|
||||
- [x] 4.5 Auto-select the only known workspace when run outside a workspace
|
||||
- [x] 4.6 Present an interactive picker when multiple workspaces are known
|
||||
- [x] 4.7 Report ambiguous workspace selection in non-interactive mode and list known workspace names
|
||||
- [x] 4.8 Report unresolved workspace selection clearly and suggest `openspec workspace setup`
|
||||
- [x] 4.9 Handle unsupported `--prepare-only`, `--json`, and `--change` flags with clear errors
|
||||
- [x] 4.10 Add command integration tests for selection, conflict, unsupported flags, and no-workspace cases
|
||||
|
||||
## 5. Opener Resolution
|
||||
|
||||
- [x] 5.1 Resolve command-line opener overrides before workspace-local preferences
|
||||
- [x] 5.2 Implement `workspace open --agent codex`
|
||||
- [x] 5.3 Implement `workspace open --agent claude`
|
||||
- [x] 5.4 Implement `workspace open --agent github-copilot`
|
||||
- [x] 5.5 Implement `workspace open --editor`
|
||||
- [x] 5.6 Keep the stored preferred opener unchanged for `--agent` and `--editor` overrides
|
||||
- [x] 5.7 Prompt interactively to choose an opener when the opener preference is unset
|
||||
- [x] 5.8 Report unset opener preference in non-interactive mode with override guidance
|
||||
- [x] 5.9 Add tests for opener precedence, prompting, non-interactive failure, and unchanged preference behavior
|
||||
|
||||
## 6. Opener Launchers
|
||||
|
||||
- [x] 6.1 Launch VS Code editor by opening the maintained `.code-workspace` file with `code`
|
||||
- [x] 6.2 Launch GitHub Copilot by opening the maintained `.code-workspace` file with VS Code
|
||||
- [x] 6.3 Launch Codex from the workspace root with valid linked paths attached
|
||||
- [x] 6.4 Launch Claude from the workspace root with valid linked paths attached
|
||||
- [x] 6.5 Use a minimal launch prompt when an agent CLI requires an initial prompt argument
|
||||
- [x] 6.6 Report skipped broken links with `openspec workspace doctor` as the repair path
|
||||
- [x] 6.7 Fail clearly when the selected opener executable is unavailable
|
||||
- [x] 6.8 Include the `.code-workspace` path in VS Code opener availability errors
|
||||
- [x] 6.9 Keep the selected opener as required when launching
|
||||
- [x] 6.10 Add unit tests for launcher command construction using test doubles for external tools
|
||||
|
||||
## 7. Documentation And Command Metadata
|
||||
|
||||
- [x] 7.1 Update workspace command help for setup `--opener`, open positional name, `--workspace`, `--agent`, and `--editor`
|
||||
- [x] 7.2 Update command registry and shell completion metadata for the new workspace open surface
|
||||
- [x] 7.3 Update workspace documentation to describe preferred openers, editor open, agent open, and `.code-workspace` behavior
|
||||
- [x] 7.4 Document that `.code-workspace` is machine-local and ignored by default
|
||||
- [x] 7.5 Document that root workspace open supports exploration and planning, with implementation started by explicit user request
|
||||
|
||||
## 8. Verification
|
||||
|
||||
- [x] 8.1 Run `node bin/openspec.js validate workspace-open-agent-context --strict`
|
||||
- [x] 8.2 Run targeted workspace command tests
|
||||
- [x] 8.3 Run targeted workspace foundation tests
|
||||
- [x] 8.4 Run command-generation or launcher tests that cover Codex, Claude, GitHub Copilot, and VS Code editor paths
|
||||
- [x] 8.5 Run cross-platform path-focused tests for workspace open surfaces
|
||||
- [x] 8.6 Run the relevant TypeScript test suite
|
||||
- [x] 8.7 Run `pnpm run build`
|
||||
@@ -0,0 +1,242 @@
|
||||
## Context
|
||||
|
||||
Workspace setup already creates a planning home, records linked repos or folders, stores a preferred opener, and maintains the root open surface. For workspace change planning to work in practice, the opened agent also needs OpenSpec workflow skills available from that workspace root.
|
||||
|
||||
Repo-local `openspec init` and `openspec update` already provide the user model for choosing agent surfaces and generating skills. Workspace setup should feel similar, but the installation target is the workspace root rather than any linked repo or folder.
|
||||
|
||||
The existing artifact workflow assumes a change lives under a repo-local `openspec/changes/<id>` path. Workspace planning needs the same workflow vocabulary, but the planning home may be a workspace root and the implementation homes may be linked repos or folders.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Install OpenSpec agent skills into the workspace root during workspace setup.
|
||||
- Use the active global profile to select which workflow skills are installed in the workspace.
|
||||
- Let users choose which agents receive skills with familiar `--tools` semantics.
|
||||
- Persist workspace-local agent skill selection so update can refresh the same agents later.
|
||||
- Let users refresh, add, or remove workspace-local skills later through `workspace update`.
|
||||
- Detect and report workspace-local skill drift from the active global profile.
|
||||
- Let `openspec config profile` offer to apply changed profile settings to the current workspace when run from inside a workspace.
|
||||
- Redirect workspace users from repo-local `openspec update` to `openspec workspace update`.
|
||||
- Add a built-in workspace planning schema for workspace-scoped changes.
|
||||
- Create workspace changes under the workspace planning path.
|
||||
- Represent affected areas without forcing implementation artifacts into linked repos.
|
||||
- Give agents machine-readable planning context through status/instructions output.
|
||||
- Preserve the workspace boundary: linked repos and folders remain untouched during setup/update.
|
||||
|
||||
**Non-Goals:**
|
||||
- Generating slash commands as part of workspace setup.
|
||||
- Honoring global `delivery: commands` by generating workspace command files.
|
||||
- Installing skills into linked repos or folders.
|
||||
- Adding workspace-local workflow profiles separate from global config.
|
||||
- Solving workspace-scoped artifact path discovery in the first setup-skill step.
|
||||
- Adding a separate artifact-context CLI command in the first version.
|
||||
- Implementing workspace apply, verify, or archive semantics end to end.
|
||||
- Changing repo-local `openspec init` or `openspec update` behavior.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Use agent-skill language in workspace UX
|
||||
|
||||
Workspace setup should ask, "Which agents should get OpenSpec skills in this workspace?" rather than using the broader "AI tools" wording. The user-visible action is installing skills for coding agents, and the target is the workspace planning home.
|
||||
|
||||
Alternative considered: reuse the exact `init` wording. That would be familiar, but it hides the important distinction between opening a workspace and installing skills into it.
|
||||
|
||||
### Reuse the existing tool id model
|
||||
|
||||
The CLI should use the existing `--tools all|none|<ids>` grammar for non-interactive setup and update. Reusing the existing tool IDs avoids inventing a second naming system for the same configured agents.
|
||||
|
||||
Alternative considered: add `--agents`. That reads better in isolation, but it creates unnecessary parallel vocabulary next to `openspec init --tools`.
|
||||
|
||||
### Let profile choose workflows and tools choose agents
|
||||
|
||||
Workspace setup/update should use the active global profile to decide which OpenSpec workflow skills are installed. The profile answers "which actions are available?" while `--tools` answers "which agents get those actions?" Keeping those concerns separate preserves the existing profile model and avoids adding workspace-local workflow selection in this slice.
|
||||
|
||||
If global profile is `core`, workspace skills should include the core workflow set. If global profile is `custom`, workspace skills should include only the configured custom workflows. `--tools none` should still mean no agent skills are installed, regardless of profile.
|
||||
|
||||
Alternative considered: add a workspace-local profile file. That might be useful later for team-shared workspace defaults, but this slice already stores machine-local agent paths and should avoid introducing another config authority before the global profile behavior works.
|
||||
|
||||
### Preselect the preferred opener when possible
|
||||
|
||||
Interactive setup should preselect the preferred opener when that opener maps to a skill-capable agent. The user can accept the default, add more agents, or deselect it.
|
||||
|
||||
Alternative considered: install skills only for the preferred opener. That is simpler, but opener choice means "how should I open this workspace" while skill selection means "which agents should understand OpenSpec here."
|
||||
|
||||
### Persist selected workspace skill agents locally
|
||||
|
||||
Workspace setup should store the selected skill-capable agents in `.openspec-workspace/local.yaml` because agent paths and installed tool surfaces are machine-local. Workspace update should use that stored selection when the user does not pass `--tools` or make a new interactive selection.
|
||||
|
||||
Explicit `--tools` on workspace setup/update should replace the stored selection. `--tools none` should store an empty selection and remove only known OpenSpec-managed workspace skill directories.
|
||||
|
||||
The local state should also record enough last-applied information to support drift detection, such as the workflow IDs installed for each selected agent and the effective global profile/delivery at the time of the last successful sync. This is diagnostic state, not a second source of truth.
|
||||
|
||||
Alternative considered: infer selected agents by scanning `.codex/skills/`, `.claude/skills/`, and similar directories. Scanning is useful as a fallback, but persisted selection gives predictable update behavior and avoids treating unrelated user-authored files as OpenSpec-managed state.
|
||||
|
||||
### Keep non-interactive setup backward-compatible
|
||||
|
||||
`openspec workspace setup --no-interactive` should not require `--tools`. If `--tools` is omitted, setup should create the workspace and skip skill installation, preserving existing scripted workspace setup behavior. Human and JSON output should say that no workspace skills were installed and that `openspec workspace update --tools <ids>` can add them later.
|
||||
|
||||
`openspec workspace update --no-interactive` without `--tools` should refresh the stored workspace skill agent selection. If no selection is stored, it should complete without installing skills and report a clear no-op with guidance to pass `--tools`.
|
||||
|
||||
Alternative considered: require `--tools` whenever workspace setup/update is non-interactive. That mirrors repo-local init, but it would break existing workspace setup scripts that predate workspace-local skill installation.
|
||||
|
||||
### Generate workspace-local skills only
|
||||
|
||||
Workspace setup/update should generate skills under the workspace root, such as `.codex/skills/` or `.claude/skills/`. It should not generate slash commands in this slice because some command adapters resolve to global locations, and workspace setup should remain local and predictable.
|
||||
|
||||
When global delivery is `commands` or `both`, workspace setup/update should still generate only skills and report that workspace command generation is not part of this slice. This keeps profile workflow selection useful without making workspace setup perform global or repo-local command writes.
|
||||
|
||||
Alternative considered: mirror `init` exactly and generate both skills and commands. That risks surprising global writes and makes the setup boundary harder to explain.
|
||||
|
||||
### Add `workspace update` for skill refresh
|
||||
|
||||
`openspec workspace update` should refresh, add, or remove workspace-local OpenSpec skills after setup. It should resolve the current workspace when run from inside a workspace, and also support named and non-interactive forms.
|
||||
|
||||
Workspace update should compare the active global profile's workflow selection with the last applied workspace skill state. If they differ, update should add/remove only OpenSpec-managed workflow skill directories for the selected agents. Workspace doctor/list/status surfaces may report the drift as a warning, and `openspec config profile` no-op inside a workspace should use the same drift check for guidance.
|
||||
|
||||
Alternative considered: reuse `openspec update` from inside the workspace. That command currently means repo/project update, while workspace update needs workspace selection, workspace JSON/status behavior, and linked-repo safety rules.
|
||||
|
||||
### Make `config profile` workspace-aware
|
||||
|
||||
`openspec config profile` should remain a global configuration command. When it runs inside a repo-local OpenSpec project and the user chooses to apply changes, it should continue to run `openspec update`.
|
||||
|
||||
When it runs inside an OpenSpec workspace and the profile or delivery settings actually change, it should prompt to apply changes to the current workspace. If confirmed, it should run `openspec workspace update` for that workspace. If declined, it should explain that the global config changed and the user can run `openspec workspace update` later.
|
||||
|
||||
The preset shortcut `openspec config profile core` should keep its non-interactive character and not launch an apply prompt. When run from inside a workspace, it should save global config and print workspace-specific follow-up guidance to run `openspec workspace update`. When run inside a repo-local project, it should keep the existing repo-local guidance.
|
||||
|
||||
For this slice, automatic workspace context should come from the workspace planning home and its own subdirectories. Running a command from inside a linked repo or folder should keep that location's repo-local behavior unless the user explicitly selects the workspace with a workspace command option. This avoids surprising repo-local commands merely because the repo is registered as a workspace link.
|
||||
|
||||
If a directory is both inside a workspace planning home and inside a repo-local OpenSpec project, the nearest planning home should determine the apply prompt. This avoids applying a workspace profile change to a linked repo when the user is intentionally operating from the workspace planning home.
|
||||
|
||||
Alternative considered: make `openspec config profile` update all known workspaces. That would be convenient in small setups, but global config changes should not fan out into multiple planning homes without an explicit per-workspace action.
|
||||
|
||||
### Resolve a planning home before acting
|
||||
|
||||
Workflow commands should resolve whether the current change belongs to a repo-local planning home or a workspace planning home before computing paths. The resolver should identify the planning root, change root, linked areas when present, and whether implementation edits are allowed. Linked repos are not implicitly treated as workspace planning homes just because they are registered in a workspace; workspace-scoped behavior is selected from the workspace planning home or through explicit workspace selection.
|
||||
|
||||
Alternative considered: add workspace-specific command branches wherever paths are used. That would make the workspace model leak into every workflow and make generated skills more fragile.
|
||||
|
||||
### Store workspace changes in the workspace planning path
|
||||
|
||||
Workspace changes should live under the workspace planning path, initially `changes/<id>` at the workspace root. Creating the workspace change should capture shared intent once and may record affected areas, but it should not create repo-local `openspec/changes/<id>` directories in linked repos.
|
||||
|
||||
Alternative considered: materialize a repo-local change in every affected repo during workspace change creation. That was easy to reason about in the POC, but it commits too early and makes exploration look like implementation.
|
||||
|
||||
### Add a workspace planning schema
|
||||
|
||||
Workspace-scoped changes should use a built-in `workspace-planning` schema by default. This keeps the workflow verbs familiar while letting workspace changes have a structure that fits cross-area planning.
|
||||
|
||||
Initial artifact shape:
|
||||
|
||||
```text
|
||||
changes/<id>/
|
||||
.openspec.yaml # schema: workspace-planning
|
||||
proposal.md # shared goal and scope
|
||||
design.md # cross-area decisions
|
||||
tasks.md # coordination tasks, optionally grouped by affected area
|
||||
specs/
|
||||
<area-or-repo>/
|
||||
<capability>/spec.md
|
||||
```
|
||||
|
||||
The first schema should stay intentionally close to the normal OpenSpec artifact shape: proposal, specs, design, and tasks. Area-specific requirements live under `specs/` and area-specific work can be represented as sections in `tasks.md`. This slice does not introduce another area manifest beside those normal planning artifacts.
|
||||
|
||||
Alternative considered: reuse `spec-driven` unchanged and make all workspace differences implicit in status output. That hides the fact that workspace planning needs different instructions for organizing requirements and tasks by affected area.
|
||||
|
||||
Alternative considered: create separate workspace workflow skills instead of a schema. That would duplicate workflow guidance and make workspace mode feel like a different product.
|
||||
|
||||
### Support nested workspace spec paths in the schema
|
||||
|
||||
The `workspace-planning` schema should define its specs artifact so nested workspace paths are first-class, not accidental. The intended output pattern is `specs/**/*.md`, and the schema instructions should explicitly describe `specs/<area-or-repo>/<capability>/spec.md` as the default convention for area-specific requirements.
|
||||
|
||||
Status and instructions output should preserve the concrete nested paths it discovers. Repo-local spec sync, archive, and validation paths that assume `specs/<capability>/spec.md` should not treat workspace-scoped specs as repo-local capability specs until a later explicit implementation, sync, or archive workflow selects an affected area and defines the destination.
|
||||
|
||||
### Use affected areas, not targets or repo slices
|
||||
|
||||
The planning model should call ownership or implementation boundaries "affected areas." Affected areas can start with registered workspace link names, but the language should leave room for folders, packages, services, apps, or docs sites. Delivery breakdown remains a separate concept and should not be called an area.
|
||||
|
||||
Alternative considered: keep "targets" because it maps to the old POC flag. That term is implementation-first and encourages users to choose repos before the plan is clear.
|
||||
|
||||
### Make status JSON the agent context contract
|
||||
|
||||
`openspec status --change <id> --json` should become the primary source of machine-readable action context. It should include the planning home, change root, concrete artifact paths, affected areas, next steps, and constraints such as allowed edit roots when implementation is later in scope.
|
||||
|
||||
Alternative considered: create a separate context command immediately. Status is already used by generated workflow skills, so enriching it first gives agents a single place to look.
|
||||
|
||||
### Keep generated skills path-agnostic
|
||||
|
||||
Generated workflow skills should ask OpenSpec where artifacts live instead of embedding repo-local paths such as `openspec/changes/<name>`. The standard skill pattern should be:
|
||||
|
||||
```text
|
||||
1. Run `openspec status --change "<name>" --json`.
|
||||
2. Use the returned planning home, artifacts, next steps, and action context.
|
||||
3. Run `openspec instructions <artifact> --change "<name>" --json` before writing an artifact.
|
||||
4. Write to the resolved path returned by the CLI.
|
||||
```
|
||||
|
||||
This keeps the same skill usable in repo-local and workspace-scoped changes. If status/instructions output later becomes too crowded, a separate context command can be introduced in a future change without changing the high-level skill rule.
|
||||
|
||||
Alternative considered: add a new `openspec context` command now. That may become useful, but it adds a new surface before we have proven that enriched status/instructions are insufficient.
|
||||
|
||||
### Guard unsupported workspace workflow actions
|
||||
|
||||
The global profile may select workflows whose workspace-scoped behavior is not implemented in this slice, such as full workspace apply, verify, or archive. Generated workspace-local skills for those workflows should be safe: they should inspect status/instructions, explain the unsupported workspace action, and avoid editing linked repos unless a later explicit implementation workflow supplies an allowed edit root.
|
||||
|
||||
This keeps the workspace skill set aligned with the user's profile while preventing repo-local fallbacks from pretending to implement workspace semantics.
|
||||
|
||||
Alternative considered: filter unsupported workflows out of workspace skill generation. That would avoid unsupported commands, but it would make the workspace skill set silently diverge from the user's profile and make drift harder to explain.
|
||||
|
||||
### Redirect repo update from workspace roots
|
||||
|
||||
`openspec update` should remain the repo/project update command. When it is run from an OpenSpec workspace planning home, it should not try to treat the workspace as a repo-local project. It should fail or redirect with clear guidance to run `openspec workspace update`.
|
||||
|
||||
Alternative considered: make `openspec update` polymorphic and perform workspace update inside workspaces. That would be convenient, but it blurs the repo/project versus workspace boundary this change is trying to make explicit.
|
||||
|
||||
### Update docs, help, and completions
|
||||
|
||||
The CLI help, command registry/completions, and user docs should include `openspec workspace update`, its `--tools` behavior, the global-profile relationship, and the skills-only workspace delivery rule.
|
||||
|
||||
Alternative considered: document this only after implementation. Because profile/update behavior is easy to confuse with repo-local update, the docs and help updates are part of the user-facing feature.
|
||||
|
||||
### Treat manual acceptance and UX review as phase gates
|
||||
|
||||
Each phase should produce a user-testable increment, even when most of the work is internal. The phase is not done until a user can exercise the named behavior through the CLI, inspect the resulting output or files, and understand what changed.
|
||||
|
||||
Each implementation phase should include a manual acceptance pass in addition to automated tests. The manual pass should exercise the real CLI flow, inspect the generated files or output, and confirm linked repos or folders stay untouched where that is part of the contract.
|
||||
|
||||
Each phase should also include a lightweight UX review of prompts, command forms, human output, JSON output, artifact paths, and next-step guidance. Any confusing UX found during review should be fixed in the same phase or recorded as an intentional follow-up before the phase is considered done.
|
||||
|
||||
Alternative considered: keep manual review only in the final verification phase. That would catch end-to-end issues late, but workspace planning is mostly workflow and agent-facing UX, so each phase needs its own human check while the behavior is still fresh.
|
||||
|
||||
### Reduce self-validation bias with evidence-based review
|
||||
|
||||
Implementation should define acceptance evidence before marking tasks done. For each phase, the implementer should capture the exact manual commands or interaction path, expected observations, and actual observations. A task is not complete merely because the implementer believes the code matches the design.
|
||||
|
||||
When practical, a separate reviewer or fresh agent context should run the manual acceptance checklist and UX review using only the change artifacts, CLI output, and observed filesystem state. If a separate reviewer is not available, the implementer should rerun the checklist from a clean temporary workspace and record the evidence in the change notes or final implementation summary.
|
||||
|
||||
Alternative considered: rely on automated tests plus the implementer's final review. Automated tests are necessary, but this change is workflow-heavy and agent-facing, so independent evidence is more useful than confidence alone.
|
||||
|
||||
## Deferred Direction
|
||||
|
||||
The earlier product notes pointed at a richer workspace model than this slice ships. Keep that direction as follow-up material, not competing current scope.
|
||||
|
||||
- Full workspace apply should select or confirm one work focus before implementation. The first work focus should be an affected area with an allowed edit root; later work may add an optional delivery phase when a large change needs sequencing. Until that model exists, workspace apply/verify/archive skills remain guarded.
|
||||
- Workspace verify and archive should wait for a clear model of partial area completion, final whole-change completion, and how workspace-scoped specs become repo-local canonical specs.
|
||||
- Scoped plan files may eventually attach at the change, phase, affected-area, or work-focus level. This slice intentionally keeps the first workspace schema close to normal OpenSpec artifacts: proposal, specs, design, and tasks.
|
||||
- Affected areas can start as registered workspace link names, but future flows may refine or derive them from planning artifacts. That derivation should avoid reintroducing target-first or repo-slice language.
|
||||
- Workflow skills may later separate generic OpenSpec workflow semantics from agent-specific affordances such as asking questions, tracking todos, or delegating work. This slice only makes generated workflow skills path-agnostic.
|
||||
- OpenSpec may need a named exploratory-notes convention for preserving unsettled thinking before it is promoted into proposal, design, specs, or tasks. This cleanup keeps the current change folder focused on standard artifacts.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Skill generation logic may drift from `init/update` → share the same template generation and tool validation helpers where practical.
|
||||
- Removing unselected skills could remove user-modified files → remove only known OpenSpec-managed workflow skill directories by explicit workflow list.
|
||||
- `--tools` is less precise than `--agents` in workspace UX → keep `--tools` for CLI consistency, but use "agents" in prompts and human output.
|
||||
- Global delivery can say `commands` while workspace update remains skills-only → report this explicitly so users know command generation is deferred, not silently broken.
|
||||
- `config profile` may run from a linked repo inside an opened workspace → resolve the current planning home carefully and apply only to that home.
|
||||
- Stored workspace skill state can become stale or hand-edited → treat it as diagnostic machine-local state and always reconcile managed files from the active global profile during update.
|
||||
- Profile-selected workflows may not yet have full workspace semantics → generated skills must guard unsupported actions and avoid repo-local fallbacks.
|
||||
- Existing generated skills still contain repo-local path assumptions → handle that as a later artifact-context step after workspace-local skills can be installed.
|
||||
- Status JSON may become too broad → keep fields plain and action-oriented, such as `planningHome`, `artifacts`, `affectedAreas`, `nextSteps`, and `actionContext`.
|
||||
- Affected area discovery may be ambiguous → start with explicit registered workspace links and allow later refinement instead of parsing free-form Markdown headings as the only source of truth.
|
||||
- A new schema can drift from repo-local workflow expectations → keep artifact IDs plain and make status/instructions carry the schema-specific paths.
|
||||
- Skill instructions may lag behind CLI behavior → audit source workflow templates for hardcoded repo-local paths and replace them with the path-agnostic status/instructions pattern.
|
||||
@@ -0,0 +1,78 @@
|
||||
## Why
|
||||
|
||||
Once repos are visible and the agent has workspace context, the user should be able to plan a cross-repo change without creating repo-local artifacts before implementation starts.
|
||||
|
||||
The user goal is:
|
||||
|
||||
```text
|
||||
Explore the product goal across repos.
|
||||
Decide the scope.
|
||||
Create one workspace-level proposal that identifies the affected areas.
|
||||
```
|
||||
|
||||
Planning should be the commitment point. Repo visibility alone should remain lightweight.
|
||||
|
||||
## What Changes
|
||||
|
||||
Add workspace-level change planning:
|
||||
|
||||
- install and refresh OpenSpec agent skills from the workspace root so agents can operate from the planning home
|
||||
- use the active global workflow profile to decide which workflow skills are installed in the workspace
|
||||
- keep `--tools` focused on which agents receive those workspace-local skills
|
||||
- add a workspace-specific planning schema for workspace changes
|
||||
- create a workspace change from the coordination root
|
||||
- capture the product goal once
|
||||
- identify affected areas by registered workspace link name where applicable
|
||||
- let the agent explore before committing to affected areas or delivery slices
|
||||
- keep the workspace as the planning source of truth
|
||||
- update workflow skill instructions to use CLI-reported artifact paths instead of hardcoded repo-local paths
|
||||
|
||||
This slice should avoid creating repo-local artifacts as a side effect of planning. Repo-local artifacts should not be created merely because a workspace change exists.
|
||||
|
||||
Workspace setup and update may write agent skill files into the workspace root, such as `.codex/skills/` or `.claude/skills/`, because those files make the workspace planning home usable by agents. That setup work must not write OpenSpec artifacts or agent skill files into linked repos or folders.
|
||||
|
||||
Interactive setup should ask which agents should get OpenSpec skills in the workspace, preselecting the preferred opener when that opener supports skills. Workspace update should let users refresh or change those installed agent skills later, including when run from inside the workspace.
|
||||
|
||||
Workspace setup and update should treat the global profile as the workflow selection source. For this slice, workspace setup and update are skills-only even when global delivery is `commands` or `both`; command generation for workspaces is deferred.
|
||||
|
||||
`openspec config profile` should remain global, but when it runs from inside an OpenSpec workspace and changes the global profile or delivery settings, it should offer to apply the new workflow selection to the current workspace by running `openspec workspace update`.
|
||||
|
||||
Workspace-local skill selection should be machine-local state: setup records which agents received skills, update refreshes that stored selection by default, and explicit `--tools` changes the stored selection. OpenSpec should detect when workspace-local skills drift from the current global profile and give clear update guidance.
|
||||
|
||||
Selected profile workflows that are not yet fully implemented for workspace-scoped changes should still be safe. Generated skills and CLI guidance must guard unsupported workspace actions instead of falling back to repo-local behavior or editing linked repos implicitly.
|
||||
|
||||
Workspace help, docs, and completions should make the distinction legible: `openspec update` remains repo/project sync, while `openspec workspace update` syncs workspace-local agent skills.
|
||||
|
||||
Planning dependency:
|
||||
|
||||
- Depends on `workspace-open-agent-context`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `workspace-change-planning`: Creates and manages workspace-level proposals for cross-repo goals.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `workspace-links`: Adds workspace setup/update behavior for workspace-local agent skill installation.
|
||||
- `cli-config`: Makes `openspec config profile` aware of workspace roots and able to apply global profile changes to the current workspace.
|
||||
- `change-creation`: Adds workspace-aware change creation semantics and affected area identification.
|
||||
- `cli-artifact-workflow`: Enriches workflow status and instructions so agents can discover planning context and artifact paths without hardcoded repo-local assumptions.
|
||||
- `artifact-graph`: Adds a built-in workspace planning schema for workspace-scoped changes.
|
||||
- `schema-resolution`: Ensures workspace-scoped change creation and workflow commands can resolve the workspace planning schema.
|
||||
- `openspec-conventions`: Defines the relationship between workspace-level planning and repo-local implementation work.
|
||||
|
||||
## Impact
|
||||
|
||||
- Workspace change creation.
|
||||
- Workspace-specific planning schema and templates.
|
||||
- Affected area metadata and validation.
|
||||
- Workspace setup and update behavior for installing or refreshing agent skills in the workspace root.
|
||||
- Global profile integration for workspace-local skill workflow selection.
|
||||
- Workspace-aware `openspec config profile` apply prompt behavior.
|
||||
- Workspace-local agent skill selection state and drift detection.
|
||||
- Guarded workflow guidance for profile workflows whose workspace behavior is not implemented in this slice.
|
||||
- Docs, help, and completions for workspace skill update behavior.
|
||||
- Agent instructions for proposing cross-repo changes without hardcoded change paths.
|
||||
- Tests that registered repos are visible before change creation and that creating a change does not imply repo-local artifact creation.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace planning schema
|
||||
The artifact graph SHALL provide a built-in workspace planning schema for workspace-scoped changes.
|
||||
|
||||
#### Scenario: Built-in workspace planning schema is available
|
||||
- **WHEN** schemas are resolved from package built-ins
|
||||
- **THEN** a schema named `workspace-planning` SHALL be available
|
||||
- **AND** it SHALL describe the artifact structure for workspace-scoped planning
|
||||
|
||||
#### Scenario: Workspace planning schema artifacts
|
||||
- **WHEN** the `workspace-planning` schema is loaded
|
||||
- **THEN** it SHALL include the normal planning artifacts for a shared proposal, workspace-scoped specs, cross-area design, and coordination tasks
|
||||
- **AND** it SHALL not require an additional area manifest outside those normal planning artifacts
|
||||
|
||||
#### Scenario: Workspace planning schema supports nested specs
|
||||
- **WHEN** the `workspace-planning` schema defines its specs artifact
|
||||
- **THEN** the specs artifact SHALL resolve workspace-scoped spec files under `specs/**/*.md`
|
||||
- **AND** schema guidance SHALL describe `specs/<area-or-repo>/<capability>/spec.md` as the default convention for area-specific requirements
|
||||
|
||||
#### Scenario: Workspace planning schema templates
|
||||
- **WHEN** artifact instructions are requested for the `workspace-planning` schema
|
||||
- **THEN** the schema SHALL provide templates that guide agents to write workspace-level planning content
|
||||
- **AND** those templates SHALL avoid instructing agents to create repo-local implementation artifacts
|
||||
- **AND** specs instructions SHALL support organizing area-specific requirements under workspace-scoped `specs/` paths
|
||||
|
||||
#### Scenario: Workspace nested spec paths stay workspace-scoped
|
||||
- **GIVEN** a workspace change has spec files under `specs/<area-or-repo>/<capability>/spec.md`
|
||||
- **WHEN** OpenSpec reports status or artifact instructions for the workspace change
|
||||
- **THEN** it SHALL preserve the concrete nested workspace spec paths
|
||||
- **AND** it SHALL not treat those files as repo-local specs to sync or archive without an explicit affected-area implementation context
|
||||
|
||||
#### Scenario: Workspace planning apply readiness
|
||||
- **WHEN** the `workspace-planning` schema defines apply readiness
|
||||
- **THEN** it SHALL require coordination tasks before implementation begins
|
||||
- **AND** the apply guidance SHALL direct agents to select an affected area before making implementation edits
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace-aware change creation
|
||||
Change creation SHALL support both repo-local and workspace planning homes.
|
||||
|
||||
#### Scenario: Creating a change from a workspace root
|
||||
- **GIVEN** the command runs from an OpenSpec workspace root
|
||||
- **WHEN** the user creates a new change
|
||||
- **THEN** OpenSpec SHALL create the change under the workspace planning path
|
||||
- **AND** it SHALL not create the change under a linked repo's `openspec/changes/` directory
|
||||
- **AND** it SHALL use the `workspace-planning` schema when no explicit schema is provided
|
||||
|
||||
#### Scenario: Creating a change from inside a workspace
|
||||
- **GIVEN** the command runs from a subdirectory of an OpenSpec workspace planning home
|
||||
- **WHEN** the user creates a new change
|
||||
- **THEN** OpenSpec SHALL resolve the current workspace as the planning home
|
||||
- **AND** it SHALL create the change under that workspace's planning path
|
||||
- **AND** it SHALL use the `workspace-planning` schema when no explicit schema is provided
|
||||
|
||||
#### Scenario: Creating a change from inside a linked repo
|
||||
- **GIVEN** a repo or folder is registered as a workspace link
|
||||
- **AND** the command runs from inside that linked repo or folder rather than from the workspace planning home
|
||||
- **WHEN** the user creates a new change without explicitly selecting a workspace
|
||||
- **THEN** OpenSpec SHALL preserve repo-local change creation behavior for that location
|
||||
- **AND** it SHALL not create a workspace-scoped change merely because the location is registered as a workspace link
|
||||
|
||||
#### Scenario: Preserving repo-local change creation
|
||||
- **GIVEN** the command runs outside an OpenSpec workspace
|
||||
- **WHEN** the user creates a new change in a repo-local OpenSpec project
|
||||
- **THEN** OpenSpec SHALL continue to create the change under `openspec/changes/`
|
||||
|
||||
#### Scenario: Rejecting invalid workspace affected areas
|
||||
- **GIVEN** a workspace change creation request includes affected area names
|
||||
- **WHEN** one or more names are not registered workspace links
|
||||
- **THEN** OpenSpec SHALL reject those invalid affected areas
|
||||
- **AND** it SHALL list the valid workspace link names
|
||||
|
||||
#### Scenario: Creating without affected areas
|
||||
- **GIVEN** the user is still exploring scope
|
||||
- **WHEN** the user creates a workspace change without affected areas
|
||||
- **THEN** OpenSpec SHALL create the workspace change
|
||||
- **AND** it SHALL allow affected areas to be identified later
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Status JSON provides planning context
|
||||
The status command SHALL provide machine-readable planning context for repo-local and workspace changes.
|
||||
|
||||
#### Scenario: Reporting planning home
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** the output SHALL identify whether the change is repo-local or workspace-scoped
|
||||
- **AND** it SHALL include the planning home root and change root
|
||||
|
||||
#### Scenario: Reporting concrete artifact paths
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** the output SHALL include concrete paths for existing artifacts
|
||||
- **AND** agents SHALL be able to read those paths without assuming `openspec/changes/<id>/`
|
||||
- **AND** workspace-scoped nested spec paths SHALL be reported without flattening the area or capability path
|
||||
|
||||
#### Scenario: Reporting workspace affected areas
|
||||
- **GIVEN** the change is workspace-scoped
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** the output SHALL include known affected areas
|
||||
- **AND** it SHALL indicate when affected areas remain unresolved without requiring an additional area manifest artifact
|
||||
|
||||
#### Scenario: Reporting next steps
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** the output SHALL include next step guidance for agents
|
||||
- **AND** the guidance SHALL use plain action language
|
||||
|
||||
### Requirement: Status JSON action context
|
||||
The status command SHALL expose action context that lets agents act without hardcoded filesystem assumptions.
|
||||
|
||||
#### Scenario: Planning action context
|
||||
- **WHEN** a workspace change is still in planning
|
||||
- **THEN** status JSON SHALL identify the planning artifacts agents may read or update
|
||||
- **AND** it SHALL indicate that linked repos and folders are context for exploration
|
||||
|
||||
#### Scenario: Implementation action context
|
||||
- **WHEN** a workspace change has a selected affected area for implementation
|
||||
- **THEN** status JSON SHALL include the allowed edit root for that area
|
||||
- **AND** it SHALL avoid authorizing edits outside that selected area
|
||||
|
||||
#### Scenario: Repo-local action context
|
||||
- **GIVEN** the change is repo-local
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** status JSON SHALL preserve existing artifact status behavior
|
||||
- **AND** it SHALL report a repo-local planning home for agents that use action context
|
||||
|
||||
### Requirement: Instructions use resolved planning paths
|
||||
Artifact and apply instructions SHALL use resolved planning paths rather than hardcoded repo-local change paths.
|
||||
|
||||
#### Scenario: Workspace artifact instructions
|
||||
- **GIVEN** the change is workspace-scoped
|
||||
- **WHEN** a user runs `openspec instructions <artifact> --change <id> --json`
|
||||
- **THEN** instruction output SHALL point to the artifact path under the workspace change root
|
||||
- **AND** it SHALL not instruct the agent to write under a linked repo unless an explicit implementation context allows it
|
||||
|
||||
#### Scenario: Repo-local artifact instructions
|
||||
- **GIVEN** the change is repo-local
|
||||
- **WHEN** a user runs `openspec instructions <artifact> --change <id> --json`
|
||||
- **THEN** instruction output SHALL preserve existing repo-local paths
|
||||
|
||||
### Requirement: Workflow skills use CLI artifact context
|
||||
Generated workflow skills SHALL use OpenSpec CLI output as the source of truth for artifact locations.
|
||||
|
||||
#### Scenario: Skills inspect status before artifact work
|
||||
- **WHEN** a generated workflow skill needs to inspect or create artifacts for a change
|
||||
- **THEN** it SHALL instruct the agent to run `openspec status --change <id> --json`
|
||||
- **AND** it SHALL use returned planning context and artifact paths rather than assuming a repo-local change path
|
||||
|
||||
#### Scenario: Skills use instructions before writing artifacts
|
||||
- **WHEN** a generated workflow skill is about to create or update an artifact
|
||||
- **THEN** it SHALL instruct the agent to run `openspec instructions <artifact> --change <id> --json`
|
||||
- **AND** it SHALL write to the resolved artifact path returned by the command
|
||||
|
||||
#### Scenario: Skills avoid hardcoded repo-local paths
|
||||
- **WHEN** generated workflow skills describe artifact locations
|
||||
- **THEN** they SHALL avoid hardcoded examples that require changes to live under `openspec/changes/<id>/`
|
||||
- **AND** any examples SHALL defer to CLI-reported paths for repo-local and workspace-scoped changes
|
||||
|
||||
#### Scenario: Skills guard unsupported workspace workflows
|
||||
- **GIVEN** a generated workflow skill is selected by the global profile
|
||||
- **AND** the workflow does not yet have full workspace-scoped behavior in this slice
|
||||
- **WHEN** the skill is used for a workspace-scoped change
|
||||
- **THEN** it SHALL tell the agent that the workspace action is not supported yet
|
||||
- **AND** it SHALL not instruct the agent to fall back to repo-local paths or edit linked repos without an explicit allowed edit root
|
||||
|
||||
### Requirement: Workspace schema instructions
|
||||
Workflow commands SHALL use the workspace planning schema instructions for workspace-scoped changes that use that schema.
|
||||
|
||||
#### Scenario: Workspace planning artifact order
|
||||
- **GIVEN** a workspace-scoped change uses schema `workspace-planning`
|
||||
- **WHEN** a user runs `openspec status --change <id> --json`
|
||||
- **THEN** the artifact list SHALL reflect the workspace planning schema
|
||||
- **AND** it SHALL include the normal proposal, specs, design, and tasks artifacts
|
||||
|
||||
#### Scenario: Workspace specs instructions
|
||||
- **GIVEN** a workspace-scoped change uses schema `workspace-planning`
|
||||
- **WHEN** a user requests instructions for the specs artifact
|
||||
- **THEN** instruction output SHALL guide the agent to organize area-specific requirements under workspace-scoped `specs/` paths
|
||||
- **AND** it SHALL not require all affected areas to be finalized before planning can continue
|
||||
- **AND** it SHALL not instruct the agent to create repo-local spec files while the change is still in workspace planning
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Config profile applies to current workspace
|
||||
The `openspec config profile` command SHALL remain global while offering an explicit workspace apply path when run from inside an OpenSpec workspace.
|
||||
|
||||
#### Scenario: Config profile run inside a workspace
|
||||
- **GIVEN** the command runs from inside an OpenSpec workspace
|
||||
- **WHEN** the user changes profile or delivery settings with interactive `openspec config profile`
|
||||
- **THEN** OpenSpec SHALL save the global config changes
|
||||
- **AND** it SHALL prompt: `Apply changes to this workspace now?`
|
||||
|
||||
#### Scenario: User confirms workspace apply
|
||||
- **GIVEN** `openspec config profile` changed global profile or delivery settings inside a workspace
|
||||
- **WHEN** the user confirms the workspace apply prompt
|
||||
- **THEN** OpenSpec SHALL run `openspec workspace update` for the current workspace
|
||||
- **AND** it SHALL not run repo-local `openspec update` unless the current planning home is repo-local
|
||||
|
||||
#### Scenario: User declines workspace apply
|
||||
- **GIVEN** `openspec config profile` changed global profile or delivery settings inside a workspace
|
||||
- **WHEN** the user declines the workspace apply prompt
|
||||
- **THEN** OpenSpec SHALL explain that global config was updated
|
||||
- **AND** it SHALL tell the user to run `openspec workspace update` later to apply the profile to workspace-local skills
|
||||
- **AND** it SHALL not modify workspace skill files
|
||||
|
||||
#### Scenario: No-op inside workspace
|
||||
- **GIVEN** the command runs from inside an OpenSpec workspace
|
||||
- **WHEN** `openspec config profile` exits with no effective config changes
|
||||
- **THEN** OpenSpec SHALL not prompt to apply changes
|
||||
- **AND** it SHALL warn if workspace-local skills are out of sync with the current global profile
|
||||
- **AND** the warning SHALL suggest `openspec workspace update`
|
||||
|
||||
#### Scenario: Core preset shortcut inside a workspace
|
||||
- **GIVEN** the command runs from inside an OpenSpec workspace
|
||||
- **WHEN** the user runs `openspec config profile core`
|
||||
- **THEN** OpenSpec SHALL save the global config change without prompting to apply immediately
|
||||
- **AND** it SHALL tell the user to run `openspec workspace update` to apply the profile to workspace-local skills
|
||||
|
||||
#### Scenario: Core preset shortcut inside a repo project
|
||||
- **GIVEN** the command runs from inside a repo-local OpenSpec project
|
||||
- **WHEN** the user runs `openspec config profile core`
|
||||
- **THEN** OpenSpec SHALL preserve existing repo-local shortcut behavior
|
||||
- **AND** it SHALL tell the user to run `openspec update` to apply the profile to project files
|
||||
|
||||
#### Scenario: Workspace planning home wins over linked repo project
|
||||
- **GIVEN** the command runs in a path under a workspace planning home where a repo-local OpenSpec project could also be detected
|
||||
- **WHEN** OpenSpec decides which apply prompt to show
|
||||
- **THEN** the nearest current planning home SHALL determine whether to offer `openspec workspace update` or repo-local `openspec update`
|
||||
- **AND** OpenSpec SHALL not apply profile changes to a linked repo when the current planning home is the workspace
|
||||
|
||||
#### Scenario: Linked repo keeps repo-local profile behavior
|
||||
- **GIVEN** a repo-local OpenSpec project is registered as a workspace link
|
||||
- **AND** the command runs from inside that linked repo rather than from the workspace planning home
|
||||
- **WHEN** OpenSpec decides which apply prompt or guidance to show
|
||||
- **THEN** OpenSpec SHALL preserve repo-local `openspec update` behavior for that repo
|
||||
- **AND** it SHALL not offer `openspec workspace update` unless the workspace is explicitly selected
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Repo update redirects from workspace planning homes
|
||||
The repo-local `openspec update` command SHALL not silently treat a workspace planning home as a repo-local OpenSpec project.
|
||||
|
||||
#### Scenario: Running update from a workspace root
|
||||
- **GIVEN** the command runs from an OpenSpec workspace root
|
||||
- **WHEN** the user runs `openspec update`
|
||||
- **THEN** OpenSpec SHALL not generate repo-local project files in the workspace root
|
||||
- **AND** it SHALL tell the user to run `openspec workspace update`
|
||||
|
||||
#### Scenario: Running update from inside a workspace planning directory
|
||||
- **GIVEN** the command runs from a subdirectory of an OpenSpec workspace planning home
|
||||
- **WHEN** the user runs `openspec update`
|
||||
- **THEN** OpenSpec SHALL not run repo-local update behavior
|
||||
- **AND** it SHALL tell the user to run `openspec workspace update`
|
||||
|
||||
#### Scenario: Running update from a repo-local project
|
||||
- **GIVEN** the command runs from inside a repo-local OpenSpec project
|
||||
- **WHEN** the user runs `openspec update`
|
||||
- **THEN** OpenSpec SHALL preserve existing repo-local update behavior
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace planning vocabulary
|
||||
OpenSpec conventions SHALL distinguish workspace planning concepts using user-facing product language.
|
||||
|
||||
#### Scenario: Naming affected areas
|
||||
- **WHEN** documentation or generated guidance refers to repos, folders, packages, services, apps, or docs sites touched by a workspace change
|
||||
- **THEN** it SHALL call them affected areas
|
||||
- **AND** it SHALL avoid using "target repo" or "repo slice" as the primary user-facing term
|
||||
|
||||
#### Scenario: Naming delivery slices
|
||||
- **WHEN** documentation or generated guidance refers to delivery increments inside a larger change
|
||||
- **THEN** it SHALL call them slices or phases only when delivery sequencing is the subject
|
||||
- **AND** it SHALL not use slice as a synonym for repo, folder, or affected area
|
||||
|
||||
### Requirement: Workspace planning and implementation boundary
|
||||
OpenSpec conventions SHALL distinguish workspace-level planning from repo-local implementation ownership.
|
||||
|
||||
#### Scenario: Workspace as shared planning home
|
||||
- **WHEN** a change spans linked repos or folders
|
||||
- **THEN** conventions SHALL describe the workspace as the shared planning home
|
||||
- **AND** repo-local implementation homes SHALL retain ownership of their code and canonical behavior
|
||||
|
||||
#### Scenario: Avoiding materialization-first language
|
||||
- **WHEN** documentation explains workspace change creation
|
||||
- **THEN** it SHALL describe the user outcome in terms of shared planning and affected areas
|
||||
- **AND** it SHALL avoid making users understand implementation terms such as materialization before they can plan
|
||||
|
||||
#### Scenario: Preserving familiar workflow verbs
|
||||
- **WHEN** workspace guidance describes OpenSpec workflows
|
||||
- **THEN** it SHALL keep the familiar verbs explore, propose, apply, verify, and archive
|
||||
- **AND** it SHALL explain that workspace context changes paths, scope, and allowed edit roots rather than creating a separate workflow family
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace planning schema resolution
|
||||
Schema resolution SHALL support the built-in workspace planning schema.
|
||||
|
||||
#### Scenario: Listing workspace planning schema
|
||||
- **WHEN** a user runs `openspec schemas`
|
||||
- **THEN** the output SHALL include `workspace-planning`
|
||||
- **AND** it SHALL identify it as a package-provided schema unless overridden by a higher-precedence schema
|
||||
|
||||
#### Scenario: Resolving workspace planning schema by name
|
||||
- **WHEN** a workflow command requests schema `workspace-planning`
|
||||
- **THEN** schema resolution SHALL resolve it using the normal project, user, then package precedence order
|
||||
|
||||
#### Scenario: Workspace default schema for new changes
|
||||
- **GIVEN** the command creates a change in a workspace planning home
|
||||
- **AND** the user did not pass an explicit `--schema`
|
||||
- **WHEN** OpenSpec resolves the schema for the new change
|
||||
- **THEN** it SHALL use `workspace-planning` as the default schema
|
||||
|
||||
#### Scenario: Explicit schema override for workspace change
|
||||
- **GIVEN** the command creates a change in a workspace planning home
|
||||
- **WHEN** the user passes an explicit `--schema <name>`
|
||||
- **THEN** OpenSpec SHALL use the explicitly requested schema
|
||||
- **AND** it SHALL validate that schema using normal schema resolution
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace change planning home
|
||||
OpenSpec SHALL support workspace-level changes whose shared plan lives in the workspace planning home.
|
||||
|
||||
#### Scenario: Creating a workspace change
|
||||
- **GIVEN** the command runs from an OpenSpec workspace
|
||||
- **WHEN** the user creates a change for workspace planning
|
||||
- **THEN** OpenSpec SHALL create the change under the workspace planning path
|
||||
- **AND** it SHALL treat the workspace as the planning home for that change
|
||||
- **AND** it SHALL use the workspace planning schema when no explicit schema is provided
|
||||
|
||||
#### Scenario: Workspace planning artifact structure
|
||||
- **GIVEN** a workspace change uses the workspace planning schema
|
||||
- **WHEN** OpenSpec reports or creates planning artifacts for that change
|
||||
- **THEN** it SHALL use workspace-level artifacts for proposal, specs, cross-area design, and coordination tasks
|
||||
- **AND** those artifacts SHALL live under the workspace change root
|
||||
- **AND** it SHALL not require an additional area manifest outside those normal planning artifacts
|
||||
|
||||
#### Scenario: Capturing the shared goal once
|
||||
- **WHEN** a workspace change is proposed
|
||||
- **THEN** OpenSpec SHALL capture the product goal at the workspace change level
|
||||
- **AND** it SHALL avoid requiring separate repo-local proposals before the affected areas are understood
|
||||
|
||||
#### Scenario: Preserving linked repos during change creation
|
||||
- **WHEN** OpenSpec creates a workspace-level change
|
||||
- **THEN** it SHALL not create repo-local OpenSpec change directories inside linked repos or folders
|
||||
- **AND** it SHALL not edit implementation files in linked repos or folders
|
||||
|
||||
### Requirement: Workspace affected areas
|
||||
OpenSpec SHALL represent ownership or implementation boundaries in a workspace change as affected areas.
|
||||
|
||||
#### Scenario: Using registered workspace links as areas
|
||||
- **GIVEN** a workspace has linked repos or folders
|
||||
- **WHEN** a workspace change identifies affected areas by registered link name
|
||||
- **THEN** OpenSpec SHALL validate those area names against the workspace links
|
||||
- **AND** it SHALL report invalid area names clearly
|
||||
|
||||
#### Scenario: Planning before all areas are known
|
||||
- **WHEN** a user is still exploring a workspace change
|
||||
- **THEN** OpenSpec SHALL allow the shared plan to exist before all affected areas are finalized
|
||||
- **AND** it SHALL keep unresolved affected area questions visible in the normal planning artifacts and status output
|
||||
|
||||
#### Scenario: Organizing requirements by area
|
||||
- **GIVEN** a workspace change has requirements owned by one or more affected areas
|
||||
- **WHEN** OpenSpec reports or creates workspace-scoped specs
|
||||
- **THEN** it SHALL allow area-specific requirements to be organized under `specs/<area-or-repo>/<capability>/spec.md`
|
||||
- **AND** it SHALL not require separate area folders outside the normal `specs/` artifact tree
|
||||
- **AND** it SHALL preserve the area-or-repo path segment as workspace planning context rather than flattening it into a repo-local capability name
|
||||
|
||||
#### Scenario: Separating areas from delivery slices
|
||||
- **WHEN** a workspace change reports affected areas
|
||||
- **THEN** OpenSpec SHALL distinguish affected areas from delivery slices or phases
|
||||
- **AND** it SHALL not require users to define delivery slices for a small cross-area change
|
||||
|
||||
### Requirement: Workspace planning source of truth
|
||||
OpenSpec SHALL keep the workspace change plan as the source of truth until implementation begins for a selected affected area.
|
||||
|
||||
#### Scenario: Exploring before implementation
|
||||
- **WHEN** an agent explores a workspace change
|
||||
- **THEN** it SHALL use workspace-level planning artifacts as the shared planning source
|
||||
- **AND** it SHALL treat linked repos and folders as available context rather than committed implementation targets
|
||||
|
||||
#### Scenario: Deferring repo-local implementation
|
||||
- **WHEN** repo-local implementation work is needed for a workspace change
|
||||
- **THEN** OpenSpec SHALL require an explicit implementation workflow with a selected affected area
|
||||
- **AND** it SHALL expose the allowed edit root for that selected area before implementation edits begin
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Workspace setup installs agent skills
|
||||
OpenSpec SHALL let users install OpenSpec agent skills into a workspace during workspace setup.
|
||||
|
||||
#### Scenario: Prompting for workspace agent skills
|
||||
- **WHEN** interactive workspace setup reaches agent skill installation
|
||||
- **THEN** OpenSpec SHALL ask which agents should get OpenSpec skills in this workspace
|
||||
- **AND** the prompt SHALL use agent-skill language rather than "AI tools" language
|
||||
|
||||
#### Scenario: Preselecting the preferred opener
|
||||
- **GIVEN** the user selected a preferred opener that supports OpenSpec skill generation
|
||||
- **WHEN** interactive workspace setup asks which agents should get skills
|
||||
- **THEN** OpenSpec SHALL preselect the matching agent
|
||||
- **AND** the user SHALL be able to select additional agents or deselect the preselected agent
|
||||
|
||||
#### Scenario: Installing selected workspace skills
|
||||
- **WHEN** workspace setup completes with one or more selected agents
|
||||
- **THEN** OpenSpec SHALL generate or refresh OpenSpec skill files under the workspace root for each selected agent
|
||||
- **AND** it SHALL report which agents received skills
|
||||
- **AND** it SHALL store the selected agents in workspace-local machine state
|
||||
|
||||
#### Scenario: Installing profile-selected workflows
|
||||
- **GIVEN** global config resolves to a workflow profile
|
||||
- **WHEN** workspace setup installs agent skills
|
||||
- **THEN** OpenSpec SHALL install workspace-local skills for the workflows selected by that profile
|
||||
- **AND** it SHALL treat `--tools` as agent selection, not workflow selection
|
||||
- **AND** it SHALL record the last applied workflow IDs for drift detection
|
||||
|
||||
#### Scenario: Installing skills only during setup
|
||||
- **WHEN** workspace setup installs agent skills
|
||||
- **THEN** OpenSpec SHALL generate skill files only
|
||||
- **AND** it SHALL not generate slash command files or global command files as part of workspace setup
|
||||
|
||||
#### Scenario: Ignoring command delivery for workspace setup
|
||||
- **GIVEN** global config delivery is `commands` or `both`
|
||||
- **WHEN** workspace setup installs agent skills
|
||||
- **THEN** OpenSpec SHALL still generate workspace-local skills only
|
||||
- **AND** it SHALL report that workspace command generation is not part of this slice
|
||||
|
||||
#### Scenario: Preserving linked repos during skill installation
|
||||
- **WHEN** workspace setup installs agent skills
|
||||
- **THEN** OpenSpec SHALL leave linked repos and folders unchanged
|
||||
- **AND** generated skills SHALL be scoped to the workspace planning home
|
||||
|
||||
#### Scenario: Non-interactive setup tool selection
|
||||
- **WHEN** non-interactive workspace setup receives `--tools all`, `--tools none`, or `--tools <ids>`
|
||||
- **THEN** OpenSpec SHALL use the selected tool set for workspace agent skill installation
|
||||
- **AND** it SHALL validate tool IDs using the same supported tool IDs as skill generation for repo initialization
|
||||
|
||||
#### Scenario: Non-interactive setup without tool selection
|
||||
- **WHEN** non-interactive workspace setup omits `--tools`
|
||||
- **THEN** OpenSpec SHALL create the workspace without installing agent skills
|
||||
- **AND** it SHALL report that no workspace skills were installed
|
||||
- **AND** it SHALL tell the user to run `openspec workspace update --tools <ids>` to install skills later
|
||||
|
||||
#### Scenario: Reporting setup skills in JSON output
|
||||
- **WHEN** non-interactive workspace setup installs agent skills with JSON output enabled
|
||||
- **THEN** OpenSpec SHALL include generated, refreshed, skipped, or failed skill installation results in machine-readable output
|
||||
|
||||
### Requirement: Workspace update manages agent skills
|
||||
OpenSpec SHALL provide a workspace update flow for refreshing agent skills after setup.
|
||||
|
||||
#### Scenario: Updating the current workspace
|
||||
- **GIVEN** the command runs from inside an OpenSpec workspace
|
||||
- **WHEN** the user runs `openspec workspace update`
|
||||
- **THEN** OpenSpec SHALL update that current workspace
|
||||
|
||||
#### Scenario: Updating a named workspace
|
||||
- **GIVEN** a workspace named `platform` is known locally
|
||||
- **WHEN** the user runs `openspec workspace update platform`
|
||||
- **THEN** OpenSpec SHALL update the `platform` workspace
|
||||
|
||||
#### Scenario: Updating a workspace selected by flag
|
||||
- **GIVEN** a workspace named `platform` is known locally
|
||||
- **WHEN** the user runs `openspec workspace update --workspace platform`
|
||||
- **THEN** OpenSpec SHALL update the `platform` workspace
|
||||
|
||||
#### Scenario: Updating selected workspace skills
|
||||
- **WHEN** workspace update completes with selected agents
|
||||
- **THEN** OpenSpec SHALL refresh OpenSpec skills for selected agents
|
||||
- **AND** it SHALL add skills for newly selected agents
|
||||
- **AND** it SHALL remove OpenSpec-managed workflow skill directories for agents that are no longer selected
|
||||
- **AND** it SHALL update the stored workspace-local selected agent list
|
||||
|
||||
#### Scenario: Updating profile-selected workflows
|
||||
- **GIVEN** global config resolves to a workflow profile
|
||||
- **WHEN** workspace update refreshes workspace-local skills
|
||||
- **THEN** OpenSpec SHALL sync the workspace-local skill workflow set to the workflows selected by that profile
|
||||
- **AND** deselected workflow skill directories SHALL be removed only when they are known OpenSpec-managed workflow skill directories
|
||||
- **AND** it SHALL update the last applied workflow IDs used for drift detection
|
||||
|
||||
#### Scenario: Ignoring command delivery for workspace update
|
||||
- **GIVEN** global config delivery is `commands` or `both`
|
||||
- **WHEN** workspace update refreshes workspace-local skills
|
||||
- **THEN** OpenSpec SHALL still update workspace-local skills only
|
||||
- **AND** it SHALL not generate slash command files or global command files
|
||||
|
||||
#### Scenario: Removing only managed skill directories
|
||||
- **WHEN** workspace update removes skills for an unselected agent
|
||||
- **THEN** OpenSpec SHALL remove only known OpenSpec-managed workflow skill directories
|
||||
- **AND** it SHALL preserve unrelated files in the agent directory
|
||||
|
||||
#### Scenario: Updating stored agent selection by flag
|
||||
- **WHEN** workspace update receives `--tools <ids>` or `--tools none`
|
||||
- **THEN** OpenSpec SHALL replace the stored workspace-local selected agent list with that selection
|
||||
- **AND** future workspace updates without `--tools` SHALL use the stored selection
|
||||
|
||||
#### Scenario: Non-interactive update tool selection
|
||||
- **WHEN** workspace update receives `--tools all`, `--tools none`, or `--tools <ids>`
|
||||
- **THEN** OpenSpec SHALL update workspace agent skills using that selected tool set
|
||||
- **AND** it SHALL avoid prompting for agent selection
|
||||
|
||||
#### Scenario: Non-interactive update without tool selection
|
||||
- **GIVEN** workspace-local selected agents are stored
|
||||
- **WHEN** non-interactive workspace update omits `--tools`
|
||||
- **THEN** OpenSpec SHALL refresh the stored selected agents using the active global profile
|
||||
- **AND** it SHALL avoid prompting for agent selection
|
||||
|
||||
#### Scenario: Non-interactive update without stored selection
|
||||
- **GIVEN** no workspace-local selected agents are stored
|
||||
- **WHEN** non-interactive workspace update omits `--tools`
|
||||
- **THEN** OpenSpec SHALL complete without installing agent skills
|
||||
- **AND** it SHALL report a no-op with guidance to pass `--tools`
|
||||
|
||||
#### Scenario: Reporting workspace skill drift
|
||||
- **GIVEN** workspace-local skill state records last applied workflow IDs
|
||||
- **AND** the active global profile resolves to a different workflow set
|
||||
- **WHEN** OpenSpec reports workspace skill state
|
||||
- **THEN** it SHALL report that workspace-local skills are out of sync with the global profile
|
||||
- **AND** it SHALL suggest `openspec workspace update`
|
||||
|
||||
#### Scenario: Reporting clean workspace skill sync
|
||||
- **GIVEN** workspace-local skill state matches the active global profile and selected agents
|
||||
- **WHEN** OpenSpec reports workspace skill state
|
||||
- **THEN** it SHALL not report profile drift
|
||||
|
||||
#### Scenario: Reporting workspace skill update results
|
||||
- **WHEN** workspace update changes agent skill state
|
||||
- **THEN** OpenSpec SHALL report which agents were refreshed, added, removed, skipped, or failed
|
||||
|
||||
#### Scenario: Reporting workspace update results in JSON output
|
||||
- **WHEN** workspace update runs with JSON output enabled
|
||||
- **THEN** OpenSpec SHALL include refreshed, added, removed, skipped, or failed skill results in machine-readable output
|
||||
|
||||
### Requirement: Workspace skill update surface is documented
|
||||
OpenSpec SHALL expose workspace skill setup/update behavior in user-facing command surfaces.
|
||||
|
||||
#### Scenario: Workspace update appears in help
|
||||
- **WHEN** a user runs `openspec workspace --help`
|
||||
- **THEN** OpenSpec SHALL list `workspace update`
|
||||
- **AND** it SHALL describe it as refreshing workspace-local agent skills
|
||||
|
||||
#### Scenario: Workspace update options appear in help
|
||||
- **WHEN** a user runs `openspec workspace update --help`
|
||||
- **THEN** OpenSpec SHALL document workspace selection options
|
||||
- **AND** it SHALL document `--tools all|none|<ids>`
|
||||
- **AND** it SHALL state that global profile selects workflows and `--tools` selects agents
|
||||
|
||||
#### Scenario: Workspace update appears in completions
|
||||
- **WHEN** shell completions are generated
|
||||
- **THEN** the workspace command registry SHALL include `workspace update`
|
||||
- **AND** it SHALL include relevant options such as `--workspace`, `--tools`, `--json`, and `--no-interactive`
|
||||
@@ -0,0 +1,133 @@
|
||||
## Phase 1: Workspace Setup Skills
|
||||
|
||||
User-testable outcome: A user can run workspace setup, choose which agents get the active profile's OpenSpec skills, and verify the selected skills are generated in the workspace root only.
|
||||
|
||||
- [x] 1.1 Add an interactive workspace setup step named "Install agent skills" that asks which agents should get OpenSpec skills in this workspace.
|
||||
- [x] 1.2 Preselect the preferred opener when that opener supports skills, while allowing users to choose different or additional agents.
|
||||
- [x] 1.3 Support non-interactive agent selection with the existing `--tools all|none|<ids>` style.
|
||||
- [x] 1.4 Validate workspace setup tool IDs using the same supported skill-generation tool set as repo initialization.
|
||||
- [x] 1.5 Resolve the active global profile and use it to choose which workflow skills workspace setup installs.
|
||||
- [x] 1.6 Ensure `openspec workspace setup` generates or refreshes OpenSpec agent skills in the workspace root for the selected agents.
|
||||
- [x] 1.7 Keep setup-time skill generation scoped to the workspace planning home; do not write skills or OpenSpec artifacts into linked repos or folders during workspace setup.
|
||||
- [x] 1.8 Keep workspace setup skill generation skills-only for this slice; do not generate slash commands or global command files even when global delivery includes commands.
|
||||
- [x] 1.9 Define how setup reports generated, refreshed, skipped, failed, and skills-only delivery work in human and JSON output.
|
||||
- [x] 1.10 Store the selected workspace skill agents and last-applied workflow IDs in workspace-local machine state.
|
||||
- [x] 1.11 Preserve non-interactive setup compatibility when `--tools` is omitted by skipping skill installation with clear guidance.
|
||||
- [x] 1.12 Manually run workspace setup in interactive and non-interactive modes and verify the selected profile workflows land only in the workspace root.
|
||||
- [x] 1.13 Review the setup UX: prompt wording, defaults, skip path, profile/delivery messaging, success output, and JSON output are clear before moving on.
|
||||
|
||||
## Phase 2: Workspace Skill Updates
|
||||
|
||||
User-testable outcome: A user can change the global profile, run workspace update in an existing workspace, and see workspace-local skills refresh to the selected workflows with clear human and JSON output.
|
||||
|
||||
- [x] 2.1 Add a workspace update flow that refreshes, adds, or removes OpenSpec agent skills in an existing workspace.
|
||||
- [x] 2.2 Let `openspec workspace update` resolve the current workspace when run from inside a workspace.
|
||||
- [x] 2.3 Support named and selected-workspace update forms such as `openspec workspace update platform` and `openspec workspace update --workspace platform`.
|
||||
- [x] 2.4 Support non-interactive update forms such as `openspec workspace update platform --tools codex,claude`.
|
||||
- [x] 2.5 Remove only known OpenSpec-managed workflow skill directories for agents that are no longer selected.
|
||||
- [x] 2.6 Sync workspace-local workflow skill directories to the current global profile selection.
|
||||
- [x] 2.7 Keep workspace update skills-only for this slice; do not generate slash commands or global command files even when global delivery includes commands.
|
||||
- [x] 2.8 Define how update reports refreshed, added, removed, skipped, failed, and skills-only delivery work in human and JSON output.
|
||||
- [x] 2.9 Use stored selected agents when workspace update runs without `--tools`, and update that stored selection when `--tools` is passed.
|
||||
- [x] 2.10 Detect workspace-local skill drift from the active global profile and report `openspec workspace update` guidance.
|
||||
- [x] 2.11 Manually run workspace update for refresh, add, remove, no-op, omitted-`--tools`, and profile-change cases and verify linked repos remain unchanged.
|
||||
- [x] 2.12 Review the update UX: command forms, current-workspace detection, profile/delivery messaging, drift messaging, removal messaging, and JSON output are understandable.
|
||||
|
||||
## Phase 3: Config Profile Workspace Apply
|
||||
|
||||
User-testable outcome: A user can run `openspec config profile` inside a workspace and choose whether to apply the changed global profile to that workspace now.
|
||||
|
||||
- [x] 3.1 Detect when `openspec config profile` runs from inside an OpenSpec workspace.
|
||||
- [x] 3.2 After an actual profile or delivery change inside a workspace, prompt to apply changes to the current workspace now.
|
||||
- [x] 3.3 When confirmed, run `openspec workspace update` for the current workspace instead of repo-local `openspec update`.
|
||||
- [x] 3.4 When declined, report that global config changed and that `openspec workspace update` applies it later.
|
||||
- [x] 3.5 Preserve existing repo-local `openspec config profile` apply behavior outside workspaces.
|
||||
- [x] 3.6 Keep `openspec config profile core` non-interactive, but print workspace-specific `openspec workspace update` guidance when run inside a workspace.
|
||||
- [x] 3.7 Warn on no-op config profile inside a workspace when workspace-local skills drift from the active global profile.
|
||||
- [x] 3.8 Manually run `openspec config profile` inside a workspace for confirm, decline, no-op, drift-warning, and `core` preset paths.
|
||||
- [x] 3.9 Review the config-profile UX: prompt wording, project/workspace distinction, no-op behavior, preset guidance, and follow-up guidance are clear.
|
||||
|
||||
## Phase 4: Workspace Change Creation
|
||||
|
||||
User-testable outcome: A user can create a workspace-level change from the coordination root, inspect its workspace planning artifacts, and confirm linked repos were not edited.
|
||||
|
||||
- [x] 4.1 Add a built-in `workspace-planning` schema and templates that keep the normal proposal/specs/design/tasks artifact shape.
|
||||
- [x] 4.2 Define the workspace-planning specs artifact with nested `specs/**/*.md` output support and instructions for `specs/<area-or-repo>/<capability>/spec.md`.
|
||||
- [x] 4.3 Add workspace-aware change creation from the workspace coordination root.
|
||||
- [x] 4.4 Default workspace-scoped change creation to the `workspace-planning` schema.
|
||||
- [x] 4.5 Store workspace-level changes under the workspace planning path rather than under linked repos or folders.
|
||||
- [x] 4.6 Capture the product goal once at the workspace change level.
|
||||
- [x] 4.7 Record or validate affected area names through workspace-scoped specs or task sections using registered workspace link names where applicable.
|
||||
- [x] 4.8 Ensure creating a workspace change does not create repo-local OpenSpec artifacts or edit linked repos.
|
||||
- [x] 4.9 Preserve repo-local change creation behavior outside workspaces.
|
||||
- [x] 4.10 Manually create a workspace change from a coordination root and verify the generated artifacts, workspace-scoped specs/tasks, affected areas, and untouched linked repos.
|
||||
- [x] 4.11 Review the change creation UX: goal capture, affected-area identification, artifact paths, and next-step guidance feel clear.
|
||||
|
||||
## Phase 5: Planning Home And Agent Context
|
||||
|
||||
User-testable outcome: A user can run status and instructions for repo-local and workspace changes and see the resolved planning home, artifact paths, affected areas, constraints, and next steps.
|
||||
|
||||
- [x] 5.1 Introduce a shared planning-home resolver that identifies repo-local versus workspace planning homes.
|
||||
- [x] 5.2 Enrich `openspec status --change <id> --json` with planning home, change root, relevant artifact paths, affected areas, next steps, and action context.
|
||||
- [x] 5.3 Enrich `openspec instructions <artifact> --change <id> --json` with resolved artifact paths for repo-local and workspace-scoped changes.
|
||||
- [x] 5.4 Keep workspace-level planning as the source of truth until an explicit implementation workflow selects an affected area.
|
||||
- [x] 5.5 Preserve nested workspace spec paths in status and instructions output without flattening them into repo-local capability paths.
|
||||
- [x] 5.6 Manually run status and instructions for both repo-local and workspace-scoped changes and verify paths and action context are correct.
|
||||
- [x] 5.7 Review the planning-context UX: human output, JSON field names, and next-step guidance are easy for users and agents to follow.
|
||||
|
||||
## Phase 6: Workflow Skill Instructions
|
||||
|
||||
User-testable outcome: A user can inspect regenerated workflow skills and verify they are path-agnostic and tell agents to use CLI-reported artifact paths.
|
||||
|
||||
- [x] 6.1 Update generated workflow skill templates to run `openspec status --change <id> --json` before artifact work and trust returned planning context.
|
||||
- [x] 6.2 Update generated workflow skill templates to run `openspec instructions <artifact> --change <id> --json` before writing artifacts and use the resolved output path.
|
||||
- [x] 6.3 Audit source workflow templates for hardcoded `openspec/changes/<name>` assumptions and replace them with CLI-reported path guidance.
|
||||
- [x] 6.4 Keep a separate artifact-context command out of this slice unless enriched status/instructions prove insufficient during implementation.
|
||||
- [x] 6.5 Manually regenerate or inspect installed workflow skills and verify they follow CLI-reported artifact paths in a workspace change.
|
||||
- [x] 6.6 Guard profile-selected workflow skills whose workspace behavior is not implemented yet so they do not fall back to repo-local paths or edit linked repos.
|
||||
- [x] 6.7 Review the agent-instruction UX: instructions are concise, path-agnostic, safe for unsupported workspace workflows, and practical for both repo-local and workspace planning.
|
||||
|
||||
## Phase 7: Verification
|
||||
|
||||
User-testable outcome: A user or reviewer can run the full manual checklist from a clean workspace and compare expected versus actual evidence for every earlier phase.
|
||||
|
||||
- [x] 7.1 Add tests that workspace setup installs skills in the workspace root and leaves linked repos unchanged.
|
||||
- [x] 7.2 Add tests that workspace update refreshes, adds, and removes only managed workspace skill directories.
|
||||
- [x] 7.3 Add tests that workspace setup/update use the current global profile for workflow skill selection while keeping workspace delivery skills-only.
|
||||
- [x] 7.4 Add tests that `openspec config profile` inside a workspace can apply changes through `openspec workspace update`.
|
||||
- [x] 7.5 Add tests for stored workspace skill agent selection, omitted-`--tools` behavior, and profile drift reporting.
|
||||
- [x] 7.6 Add tests that `openspec update` from a workspace planning home redirects to `openspec workspace update`.
|
||||
- [x] 7.7 Add tests that unsupported workspace workflow skills are guarded and do not instruct repo-local fallback edits.
|
||||
- [x] 7.8 Add tests that registered repos are visible before change creation.
|
||||
- [x] 7.9 Add tests that workspace change creation does not imply repo-local artifact creation.
|
||||
- [x] 7.10 Add tests that the workspace-planning schema resolves nested `specs/<area-or-repo>/<capability>/spec.md` files as workspace-scoped specs.
|
||||
- [x] 7.11 Add cross-platform path tests for workspace-root skill paths and workspace change paths.
|
||||
- [x] 7.12 Update CLI docs, command help, and shell completion coverage for `workspace update`, `--tools`, profile behavior, and workspace skills-only delivery.
|
||||
- [x] 7.13 Run `openspec validate workspace-change-planning --strict`.
|
||||
- [x] 7.14 Run the full manual acceptance checklist across setup, update, config profile, change creation, planning context, and workflow skills before marking the change complete.
|
||||
- [x] 7.15 Complete a final UX review across the whole workflow and record any follow-up fixes or intentional deferrals.
|
||||
- [x] 7.16 Before implementation sign-off, record the manual commands or interaction paths, expected observations, and actual observations for each phase.
|
||||
- [x] 7.17 Have a separate reviewer or fresh agent context rerun the manual acceptance and UX checklist when available; otherwise rerun it from a clean temporary workspace and report the evidence.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
Completion evidence was recorded on 2026-05-14.
|
||||
|
||||
Automated checks:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
pnpm vitest run test/commands/workspace.test.ts test/commands/artifact-workflow.test.ts test/core/workspace/skills.test.ts test/core/planning-home.test.ts test/core/templates/skill-templates-parity.test.ts
|
||||
node dist/cli/index.js validate workspace-change-planning --strict
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Clean workspace rerun covered non-interactive workspace setup, workspace doctor, config profile update guidance, workspace update redirection, workspace change creation with `--areas api,web`, status/instructions JSON for nested workspace specs, linked repo cleanliness, and guarded unsupported workflow skills.
|
||||
|
||||
Observed results:
|
||||
|
||||
- Build, targeted tests, strict validation, and whitespace checks passed.
|
||||
- Workspace setup/update generated skills only in the workspace root and left linked repos untouched.
|
||||
- Workspace change creation used schema `workspace-planning`, reported affected areas `api` and `web`, preserved nested `specs/api/login/spec.md`, and kept `actionContext.allowedEditRoots` empty during planning.
|
||||
- Generated workflow skills used CLI-reported paths and workspace guards rather than hardcoded `openspec/changes/<name>` paths.
|
||||
- Fresh-agent rerun was not available; the clean temporary workspace rerun served as the fallback independent acceptance pass.
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-06-29
|
||||
@@ -0,0 +1,59 @@
|
||||
## Context
|
||||
|
||||
OpenSpec supports AI coding assistants by generating two artifact types per tool: skill files (for agent instruction loading) and command files (for slash-command invocation). Each tool has a `ToolCommandAdapter` that controls the output path and file format.
|
||||
|
||||
Oh My Pi (OMP) is a terminal AI coding agent that uses a `.omp/` project directory. Its command system uses the filename stem as the slash command name (e.g., `opsx-propose.md` → `/opsx-propose`), which requires command body references to be in hyphenated form (`/opsx-propose` rather than `/opsx:propose`). This is the same pattern already used by Pi and OpenCode.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Add a `ToolCommandAdapter` for Oh My Pi producing `.omp/commands/opsx-<id>.md` with `description` frontmatter.
|
||||
- Inject `**Provided arguments**: $@` after the `**Input**:` heading in command bodies so user-supplied arguments are visible to the agent when a command is invoked with arguments.
|
||||
- Register the adapter so `init` and `update` can generate command files and skill files for OMP.
|
||||
- Apply `transformToHyphenCommands` to OMP skill bodies so `/opsx:` references become `/opsx-` for consistency with the command naming convention.
|
||||
- Add OMP to `AI_TOOLS` so it appears in tool selection and auto-detection.
|
||||
|
||||
**Non-Goals:**
|
||||
- Changing the file format used by Pi or OpenCode.
|
||||
- Adding OMP-specific frontmatter fields beyond `description`.
|
||||
- Auto-detecting OMP presence (the `.omp/` directory is sufficient as `skillsDir`).
|
||||
|
||||
## Decisions
|
||||
|
||||
### Reuse the existing `transformToHyphenCommands` transformer for skill files
|
||||
|
||||
**Decision**: Add `'oh-my-pi'` to the `tool.value` conditional in `init.ts` and `update.ts` that selects the hyphen transformer.
|
||||
|
||||
**Rationale**: Pi and OpenCode follow the same filename-as-command-name convention and are already handled by this branch. OMP has an identical convention. Extending the same conditional is minimal-diff and keeps the pattern consistent.
|
||||
|
||||
**Alternative considered**: Storing the transformer flag on the `AIToolOption` object (e.g., `useHyphenCommands: true`). This is cleaner long-term but is a larger refactor than this change warrants. It can be done separately if more tools adopt this convention.
|
||||
|
||||
### Use `description`-only frontmatter in command files
|
||||
|
||||
**Decision**: The `formatFile` method outputs only a `description` YAML field in frontmatter.
|
||||
|
||||
**Rationale**: OMP's command format uses filename for the slash command name and `description` for display. No additional frontmatter fields (name, category, tags) are needed, matching the minimalist approach used by Pi.
|
||||
|
||||
### Inject `$@` into command bodies (matching Pi)
|
||||
|
||||
**Decision**: Apply the same `injectArgs` logic as Pi's adapter — append `**Provided arguments**: $@` on the line after the `**Input**:` heading, skipping injection if `$@` or `$ARGUMENTS` is already present.
|
||||
|
||||
**Rationale**: OpenSpec command templates contain an `**Input**:` heading that describes what arguments the command accepts (e.g., `**Input**: The argument after /opsx-propose is the change name…`). Without injecting `$@`, a user running `/opsx-propose my-feature` passes `my-feature` as `$@` but the agent never sees it — the argument is silently discarded. OMP's prompt template spec explicitly supports `$@` and positional forms. Pi faces the same problem and already solves it with identical injection logic.
|
||||
|
||||
**Alternative considered**: Leaving injection out and relying on users to add `$@` manually to the template. Rejected: this would silently break argument passing for all OMP commands and diverge from Pi's established behavior.
|
||||
|
||||
### Tool ID is `'oh-my-pi'`, skills directory is `'.omp'`
|
||||
|
||||
**Decision**: `value: 'oh-my-pi'` in `AI_TOOLS`; `skillsDir: '.omp'`.
|
||||
|
||||
**Rationale**: The tool ID uses the full kebab-case name for human clarity. The `.omp/` directory is the short canonical path users will see on disk. The two are independent and follow the precedent set by `kilocode` (ID) → `.kilocode` (dir).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **`.omp/` directory collision**: If a project uses `.omp/` for another purpose, OMP detection will yield a false positive. → Mitigation: This is consistent with how every other tool is detected; no special handling is warranted.
|
||||
- **Conditional growth in init.ts / update.ts**: Adding a third value to the `tool.value === 'opencode' || tool.value === 'pi'` checks makes the long-term refactor to a per-tool flag more urgent. → Mitigation: Document in tasks; the refactor is low-risk and can follow separately.
|
||||
- **Adapter missing `escapeYamlValue`**: If a command description contains special YAML characters, the description frontmatter could be malformed. → Mitigation: `escapeYamlValue` is applied in this implementation (task 1.2), consistent with Pi adapter.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None — implementation is well-defined by the existing Pi/OpenCode/OMP pattern.
|
||||
@@ -0,0 +1,34 @@
|
||||
## Why
|
||||
|
||||
Oh My Pi (OMP) is a terminal AI coding agent whose users expect OpenSpec workflows to be available as slash commands. Without an adapter, users who have OMP configured in their project cannot generate OMP-native command files or get the correct skill transformations from `openspec init` or `openspec update`.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add a `ToolCommandAdapter` for Oh My Pi that generates command files at `.omp/commands/opsx-<id>.md` with YAML `description` frontmatter, hyphen-based command references, and `$@` argument injection after the `**Input**:` heading (matching Pi's convention so user-supplied arguments are visible to the agent).
|
||||
- Register `oh-my-pi` in `AI_TOOLS` with `skillsDir: '.omp'` so detection and skill generation work.
|
||||
- Register the new adapter in `CommandAdapterRegistry` and `adapters/index.ts`.
|
||||
- Add Oh My Pi to the `transformToHyphenCommands` whitelist in `init.ts` and `update.ts` so skill files use the correct `/opsx-*` invocation form that matches OMP's filename-based command naming.
|
||||
- Add test coverage for the new adapter.
|
||||
- Update `docs/supported-tools.md` with the new tool's directory reference.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `oh-my-pi-tool`: Command and skill generation support for the Oh My Pi (OMP) AI coding agent, following its `.omp/commands/opsx-<id>.md` format with `description` frontmatter, hyphen-based command references, and `$@` argument injection.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `cli-init`: Oh My Pi is added to the supported tool list and the hyphen-command transformer whitelist.
|
||||
- `cli-update`: Oh My Pi is added to the hyphen-command transformer whitelist for skill regeneration.
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/command-generation/adapters/oh-my-pi.ts` — new adapter
|
||||
- `src/core/command-generation/adapters/index.ts` — export new adapter
|
||||
- `src/core/command-generation/registry.ts` — register adapter
|
||||
- `src/core/config.ts` — add `oh-my-pi` entry to `AI_TOOLS`
|
||||
- `src/core/init.ts` — extend hyphen-command transformer conditional
|
||||
- `src/core/update.ts` — extend hyphen-command transformer conditional (two call sites)
|
||||
- `test/core/command-generation/adapters.test.ts` — adapter unit tests
|
||||
- `docs/supported-tools.md` — add Oh My Pi row to directory reference table
|
||||
@@ -0,0 +1,15 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Oh My Pi tool supported in init
|
||||
The `openspec init` command SHALL support Oh My Pi as a configurable tool, generating both skill files and command files using Oh My Pi's conventions when selected.
|
||||
|
||||
#### Scenario: Selecting Oh My Pi during init
|
||||
- **WHEN** a user selects Oh My Pi during `openspec init`
|
||||
- **THEN** skill files are written to `.omp/skills/openspec-<id>/SKILL.md` for each active command
|
||||
- **AND** command files are written to `.omp/commands/opsx-<id>.md` for each active command
|
||||
- **AND** skill file bodies use hyphen-based `/opsx-<id>` command references
|
||||
- **AND** command file bodies have `**Provided arguments**: $@` injected after any `**Input**:` heading
|
||||
|
||||
#### Scenario: Oh My Pi listed when .omp directory is detected
|
||||
- **WHEN** the project root contains a `.omp/` directory
|
||||
- **THEN** Oh My Pi is pre-checked in the tool selection during `openspec init`
|
||||
@@ -0,0 +1,13 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Oh My Pi tool supported in update
|
||||
The `openspec update` command SHALL refresh Oh My Pi skill files and command files when Oh My Pi is configured, using Oh My Pi's hyphen-based command reference convention.
|
||||
|
||||
#### Scenario: Updating Oh My Pi skill files
|
||||
- **WHEN** `openspec update` runs and Oh My Pi is a configured tool
|
||||
- **THEN** skill files in `.omp/skills/openspec-<id>/SKILL.md` are refreshed with the latest templates
|
||||
- **AND** skill file bodies use hyphen-based `/opsx-<id>` command references
|
||||
|
||||
#### Scenario: Updating Oh My Pi command files
|
||||
- **WHEN** `openspec update` runs and Oh My Pi is a configured tool
|
||||
- **THEN** command files are written to `.omp/commands/opsx-<id>.md` for each workflow in the active profile, creating them if they do not yet exist and overwriting them if they do
|
||||
@@ -0,0 +1,48 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Oh My Pi command file generation
|
||||
OpenSpec SHALL generate command files for Oh My Pi in `.omp/commands/opsx-<id>.md`, one per active workflow command.
|
||||
|
||||
Each file SHALL include a YAML frontmatter block with a `description` field. The command body SHALL transform `/opsx:` references to `/opsx-` to match Oh My Pi's filename-based slash command naming (e.g., `opsx-propose.md` → `/opsx-propose`). It SHALL inject `**Provided arguments**: $@` on the line immediately following any `**Input**:` heading, unless `$@` or `$ARGUMENTS` is already present in the body.
|
||||
|
||||
#### Scenario: Command file path follows OMP convention
|
||||
- **WHEN** OpenSpec generates a command file for Oh My Pi for workflow command `propose`
|
||||
- **THEN** the file is written to `.omp/commands/opsx-propose.md`
|
||||
|
||||
#### Scenario: Command file format includes description frontmatter
|
||||
- **WHEN** OpenSpec writes a command file for Oh My Pi
|
||||
- **THEN** the file begins with a YAML frontmatter block containing only a `description` field
|
||||
- **AND** the body follows the closing `---`
|
||||
|
||||
#### Scenario: Command body uses hyphen-based references
|
||||
- **WHEN** OpenSpec writes a command file for Oh My Pi whose body contains `/opsx:apply` or similar colon-style references
|
||||
- **THEN** those references are transformed to `/opsx-apply` in the output file
|
||||
|
||||
#### Scenario: Command body exposes user arguments via $@
|
||||
- **WHEN** OpenSpec writes a command file for Oh My Pi whose body contains a `**Input**:` heading and no existing `$@` or `$ARGUMENTS` reference
|
||||
- **THEN** `**Provided arguments**: $@` is injected on the line immediately after the `**Input**:` heading
|
||||
- **AND** when the user invokes `/opsx-propose my-feature`, the agent receives `my-feature` as the value of `$@`
|
||||
|
||||
### Requirement: Oh My Pi skill file generation
|
||||
OpenSpec SHALL generate skill files for Oh My Pi in `.omp/skills/openspec-<id>/SKILL.md`, one per active workflow command.
|
||||
|
||||
Skill file bodies SHALL have `/opsx:` references transformed to `/opsx-` so that skill invocations refer to the correct hyphen-based slash command names.
|
||||
|
||||
#### Scenario: Skill file path follows OMP convention
|
||||
- **WHEN** OpenSpec generates a skill file for Oh My Pi for workflow command `explore`
|
||||
- **THEN** the file is written to `.omp/skills/openspec-explore/SKILL.md`
|
||||
|
||||
#### Scenario: Skill body uses hyphen-based references
|
||||
- **WHEN** OpenSpec writes a skill file for Oh My Pi whose body contains `/opsx:explore`
|
||||
- **THEN** the reference is transformed to `/opsx-explore` in the output file
|
||||
|
||||
### Requirement: Oh My Pi tool detection
|
||||
OpenSpec SHALL detect an Oh My Pi installation when the `.omp/` directory exists at the project root, and SHALL present Oh My Pi as a selectable tool in `openspec init` and `openspec update`.
|
||||
|
||||
#### Scenario: Auto-detection when .omp directory exists
|
||||
- **WHEN** the project root contains a `.omp/` directory
|
||||
- **THEN** Oh My Pi is listed as a detected tool during `openspec init` and `openspec update`
|
||||
|
||||
#### Scenario: Oh My Pi appears in the tool selection list
|
||||
- **WHEN** a user runs `openspec init` interactively
|
||||
- **THEN** Oh My Pi appears as a selectable option in the tool list
|
||||
@@ -0,0 +1,30 @@
|
||||
## 1. Adapter
|
||||
|
||||
- [x] 1.1 Create `src/core/command-generation/adapters/oh-my-pi.ts` with `ohMyPiAdapter` (toolId `'oh-my-pi'`, path `.omp/commands/opsx-<id>.md`, description-only frontmatter, `transformToHyphenCommands` on body)
|
||||
- [x] 1.2 Use `escapeYamlValue` for the `description` frontmatter field (consistent with Pi adapter)
|
||||
- [x] 1.3 Export `ohMyPiAdapter` from `src/core/command-generation/adapters/index.ts`
|
||||
- [x] 1.4 Import and register `ohMyPiAdapter` in `src/core/command-generation/registry.ts`
|
||||
- [x] 1.5 In `formatFile`, inject `**Provided arguments**: $@` on the line after the `**Input**:` heading (skip if `$@` or `$ARGUMENTS` already present) — matching Pi adapter's `injectPiArgs` logic
|
||||
|
||||
## 2. Tool Registration
|
||||
|
||||
- [x] 2.1 Add `{ name: 'Oh My Pi', value: 'oh-my-pi', available: true, successLabel: 'Oh My Pi', skillsDir: '.omp' }` to `AI_TOOLS` in `src/core/config.ts` (alphabetical by name, between Mistral Vibe and OpenCode)
|
||||
|
||||
## 3. Skill Transformer Wiring
|
||||
|
||||
- [x] 3.1 In `src/core/init.ts`, extend the skill transformer conditional to include `tool.value === 'oh-my-pi'` alongside `'opencode'` and `'pi'` (one occurrence, in `generateSkillsAndCommands`)
|
||||
- [x] 3.2 In `src/core/update.ts`, extend the skill transformer conditional to include `tool.value === 'oh-my-pi'` alongside `'opencode'` and `'pi'` (two occurrences: primary update loop and `upgradeLegacyTools`)
|
||||
|
||||
## 4. Tests
|
||||
|
||||
- [x] 4.1 In `test/core/command-generation/adapters.test.ts`, add unit tests for `ohMyPiAdapter`: verify `toolId`, `getFilePath` output uses `path.join('.omp', 'commands', 'opsx-<id>.md')`, and `formatFile` produces correct description frontmatter and transformed body
|
||||
- [x] 4.2 Verify all path assertions in the new tests use `path.join()` (not hardcoded slashes) for cross-platform correctness
|
||||
|
||||
## 5. Documentation
|
||||
|
||||
- [x] 5.1 Add Oh My Pi row to the tool directory reference table in `docs/supported-tools.md`: `| Oh My Pi (\`oh-my-pi\`) | \`.omp/skills/openspec-*/SKILL.md\` | \`.omp/commands/opsx-<id>.md\` |`
|
||||
|
||||
## 6. Verification
|
||||
|
||||
- [x] 6.1 Run `pnpm test` and confirm all tests pass, including the new adapter tests
|
||||
- [x] 6.2 Run `pnpm build` to confirm TypeScript compilation succeeds with the new adapter
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-02-25
|
||||
@@ -0,0 +1,48 @@
|
||||
## Context
|
||||
|
||||
The OpenCode adapter in `src/core/command-generation/adapters/opencode.ts` currently generates command files at `.opencode/command/opsx-<id>.md` (singular `command`). OpenCode's official documentation uses `.opencode/commands/` (plural), and every other adapter in the codebase follows the plural convention for commands directories. The legacy cleanup module in `src/core/legacy-cleanup.ts` also references the singular form for detecting old artifacts.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Align the OpenCode adapter path with OpenCode's official `.opencode/commands/` convention
|
||||
- Add the old singular path `.opencode/command/` to legacy cleanup so existing installations are properly cleaned
|
||||
- Update documentation to reflect the corrected path
|
||||
- Update test assertions to match the new path
|
||||
|
||||
**Non-Goals:**
|
||||
- Changing the OpenCode skill path (`.opencode/skills/`) — already correct
|
||||
- Modifying any other adapter's directory structure
|
||||
- Adding migration prompts or interactive upgrade flows
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Direct path rename in adapter
|
||||
|
||||
**Decision:** Change `path.join('.opencode', 'command', ...)` to `path.join('.opencode', 'commands', ...)` in the adapter's `getFilePath` method.
|
||||
|
||||
**Rationale:** This is a single-line change that aligns with the established pattern across all other adapters. No abstraction or indirection needed.
|
||||
|
||||
**Alternatives considered:**
|
||||
- Add a configuration option for the directory name — rejected as over-engineering for a bug fix
|
||||
- Keep singular and add plural as alias — rejected as it creates ambiguity about which is canonical
|
||||
|
||||
### 2. Legacy cleanup via existing constant map
|
||||
|
||||
**Decision:** Update the `LEGACY_SLASH_COMMAND_PATHS` entry for `'opencode'` from `'.opencode/command/openspec-*.md'` to `'.opencode/command/opsx-*.md'` (the old singular path becomes the legacy pattern) and ensure the new path is handled by the current command generation pipeline.
|
||||
|
||||
**Rationale:** The existing legacy cleanup infrastructure uses `LEGACY_SLASH_COMMAND_PATHS` as an explicit lookup. The old singular-path pattern already matches the legacy format (`openspec-*` prefix from the old SlashCommandRegistry era). The current command generation uses the `opsx-*` prefix, so we also need to add a legacy pattern for `opsx-*` files in the old singular directory.
|
||||
|
||||
**Alternatives considered:**
|
||||
- Add a separate migration script — rejected; the existing legacy cleanup mechanism handles this scenario
|
||||
|
||||
### 3. Documentation update
|
||||
|
||||
**Decision:** Update the `docs/supported-tools.md` table entry for OpenCode from `.opencode/command/opsx-<id>.md` to `.opencode/commands/opsx-<id>.md`.
|
||||
|
||||
**Rationale:** Documentation must match the actual generated paths.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Existing installations have files at old path]** → Mitigated by legacy cleanup detecting `.opencode/command/` artifacts. On next `openspec init`, old files are cleaned up and new files written to `.opencode/commands/`.
|
||||
- **[Users referencing old path in custom scripts]** → Low risk. The old path was incorrect per OpenCode's specification, so custom references were already misaligned.
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
The OpenCode adapter uses `.opencode/command/` (singular) for its commands directory, but OpenCode's official documentation specifies `.opencode/commands/` (plural). Every other adapter in the codebase also uses plural directory names (`.claude/commands/`, `.cursor/commands/`, `.factory/commands/`, etc.). This inconsistency was introduced in Oct 2025 without documented rationale. Fixes [#748](https://github.com/Fission-AI/OpenSpec/issues/748).
|
||||
|
||||
## What Changes
|
||||
|
||||
- OpenCode adapter path changes from `.opencode/command/` to `.opencode/commands/`
|
||||
- Legacy cleanup adds `.opencode/command/` (old singular path) for backward compatibility
|
||||
- Documentation updated to reflect the new plural path
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_None._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `command-generation`: OpenCode adapter path changes from singular `command/` to plural `commands/` to match OpenCode's official directory convention
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/command-generation/adapters/opencode.ts` — adapter path
|
||||
- `src/core/legacy-cleanup.ts` — legacy cleanup pattern + add old singular path
|
||||
- `docs/supported-tools.md` — documentation table
|
||||
- `test/core/command-generation/adapters.test.ts` — test assertion
|
||||
@@ -0,0 +1,63 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: ToolCommandAdapter interface
|
||||
|
||||
The system SHALL define a `ToolCommandAdapter` interface for per-tool formatting.
|
||||
|
||||
#### Scenario: Adapter interface structure
|
||||
|
||||
- **WHEN** implementing a tool adapter
|
||||
- **THEN** `ToolCommandAdapter` SHALL require:
|
||||
- `toolId`: string identifier matching `AIToolOption.value`
|
||||
- `getFilePath(commandId: string)`: returns file path for command (relative from project root, or absolute for global-scoped tools like Codex)
|
||||
- `formatFile(content: CommandContent)`: returns complete file content with frontmatter
|
||||
|
||||
#### Scenario: Claude adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Claude Code
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields
|
||||
- **AND** file path SHALL follow pattern `.claude/commands/opsx/<id>.md`
|
||||
|
||||
#### Scenario: Cursor adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Cursor
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name` as `/opsx-<id>`, `id`, `category`, `description` fields
|
||||
- **AND** file path SHALL follow pattern `.cursor/commands/opsx-<id>.md`
|
||||
|
||||
#### Scenario: Windsurf adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for Windsurf
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `name`, `description`, `category`, `tags` fields
|
||||
- **AND** file path SHALL follow pattern `.windsurf/workflows/opsx-<id>.md`
|
||||
|
||||
#### Scenario: OpenCode adapter formatting
|
||||
|
||||
- **WHEN** formatting a command for OpenCode
|
||||
- **THEN** the adapter SHALL output YAML frontmatter with `description` field
|
||||
- **AND** file path SHALL follow pattern `.opencode/commands/opsx-<id>.md` using `path.join('.opencode', 'commands', ...)` for cross-platform compatibility
|
||||
- **AND** the adapter SHALL transform colon-based command references (`/opsx:name`) to hyphen-based (`/opsx-name`) in the body
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Legacy cleanup for renamed OpenCode command directory
|
||||
|
||||
The legacy cleanup module SHALL detect and remove old OpenCode command files from the previous singular `.opencode/command/` directory path.
|
||||
|
||||
#### Scenario: Detect old singular-path OpenCode command files
|
||||
|
||||
- **WHEN** running legacy artifact detection on a project with files matching `.opencode/command/opsx-*.md` or `.opencode/command/openspec-*.md`
|
||||
- **THEN** the system SHALL include those files in the legacy slash command files list via `LEGACY_SLASH_COMMAND_PATHS`
|
||||
- **AND** `LegacySlashCommandPattern.pattern` SHALL accept `string | string[]` to support multiple glob patterns per tool
|
||||
|
||||
#### Scenario: Clean up old OpenCode command files on init
|
||||
|
||||
- **WHEN** a user runs `openspec init` in a project with old `.opencode/command/` artifacts
|
||||
- **THEN** the system SHALL remove the old files
|
||||
- **AND** generate new command files at `.opencode/commands/`
|
||||
|
||||
#### Scenario: Auto-cleanup legacy artifacts in non-interactive mode
|
||||
|
||||
- **WHEN** a user runs `openspec init` in non-interactive mode (e.g., CI) and legacy artifacts are detected
|
||||
- **THEN** the system SHALL auto-cleanup legacy artifacts without requiring `--force`
|
||||
- **AND** legacy slash command files (100% OpenSpec-managed) SHALL be removed
|
||||
- **AND** config file cleanup SHALL only remove OpenSpec markers (never delete user files)
|
||||
@@ -0,0 +1,19 @@
|
||||
## 1. Adapter Fix
|
||||
|
||||
- [x] 1.1 Update `src/core/command-generation/adapters/opencode.ts`: change `path.join('.opencode', 'command', ...)` to `path.join('.opencode', 'commands', ...)` and update the JSDoc comment
|
||||
|
||||
## 2. Legacy Cleanup
|
||||
|
||||
- [x] 2.1 Update `src/core/legacy-cleanup.ts`: update the `'opencode'` entry in `LEGACY_SLASH_COMMAND_PATHS` to detect both `opsx-*.md` and `openspec-*.md` patterns at `.opencode/command/` for backward compatibility
|
||||
|
||||
## 3. Documentation
|
||||
|
||||
- [x] 3.1 Update `docs/supported-tools.md`: change OpenCode command path from `.opencode/command/opsx-<id>.md` to `.opencode/commands/opsx-<id>.md`
|
||||
|
||||
## 4. Tests
|
||||
|
||||
- [x] 4.1 Update `test/core/command-generation/adapters.test.ts`: change the OpenCode file path assertion from `path.join('.opencode', 'command', 'opsx-explore.md')` to `path.join('.opencode', 'commands', 'opsx-explore.md')`
|
||||
|
||||
## 5. Changeset
|
||||
|
||||
- [x] 5.1 Create a changeset file (`.changeset/fix-opencode-commands-directory.md`) with a patch bump describing the path fix
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-06-29
|
||||
@@ -0,0 +1,77 @@
|
||||
# Design: Spec parser reading fidelity
|
||||
|
||||
## The requirement reader is implemented twice
|
||||
|
||||
| | spec reader: `MarkdownParser.parseRequirements` → `req.text` | delta reader: `Validator.extractRequirementText` / `countScenarios` |
|
||||
|---|---|---|
|
||||
| Recognition | every level-3 child of the section | canonical `REQUIREMENT_HEADER_REGEX` `/^###\s*Requirement:\s*(.+)$/i` |
|
||||
| Body capture | first non-empty line | first substantial line |
|
||||
| Skip `**metadata**:` | no | yes |
|
||||
| Fenced code in body | not skipped | not skipped |
|
||||
| Fenced `#### Scenario:` | not counted (parseSections fence-masks it) | **counted** (`/^####\s+/gm` is fence-unaware) |
|
||||
| `SHALL`/`MUST` | `text.includes('SHALL')` (substring) | `/\b(SHALL\|MUST)\b/` (word boundary) |
|
||||
| Reached by | `validate <spec>`, `archive` | `validate <change>` |
|
||||
|
||||
`ChangeParser extends MarkdownParser` and reuses `parseRequirements`, so there is no third reader. Every row where the two columns differ is a reproduced defect.
|
||||
|
||||
## Reproductions (against `main`)
|
||||
|
||||
- **#361** — `### Requirement: …` with `SHALL` on body line 2 → `validate <change>` `✗ must contain SHALL or MUST`; `validate <spec>` `✗ requirements.0.text: …`.
|
||||
- **#418** — metadata lines before a `MUST` description → `validate <change>` **valid**; `validate <spec>` `✗`, `req.text` = `**ID**: REQ-FILE-001`.
|
||||
- **#312** — fenced block (with `#` comments) before the prose line → both paths `✗`; `req.text` = `` ```bash ``. (Distinct from the already-fixed section-count manifestation.)
|
||||
- **Fenced scenario** — requirement whose only `#### Scenario:` is inside a ` ```markdown ` block → `validate <change>` **valid** (counts the fenced scenario); `validate <spec>` `✗ requirements.0.scenarios: must have at least one scenario`. The delta reader passes a malformed requirement.
|
||||
- **#498** — stray `### Documentation Requirements` divider → `validate <change>` **valid**; `archive` prints non-blocking phantom `Proposal warnings in proposal.md`; `validate <spec>` blocking `✗`. (Also: `show`/`view` count the divider as a requirement — `count=2` with `text='Documentation Notes'`.)
|
||||
|
||||
## Approach
|
||||
|
||||
### Part A — one shared, fence-aware extraction
|
||||
|
||||
A single helper takes the requirement block's lines plus the fence mask and returns the full body: lines from after the header to the first markdown header found on a **non-fence-masked** line (usually `#### Scenario:`, but also a stray `###` divider the delta reader absorbed into the block — its notes must not feed the keyword check), skipping fence-masked lines and blank lines. `**metadata**:` lines are skipped only when other body text remains; a requirement written entirely as `**Constraint**: The system MUST ...` keeps that line as its body. When the body comes back empty, `MarkdownParser` still falls back to the header title for display and bare-header compatibility; validator body-keyword checks for canonical `### Requirement:` blocks use the body-only extraction so #1280's "keyword only in header" hint remains intact on both validation paths. A companion fence-aware scenario counter counts only non-fence-masked `####` headers (deliberately *any* `####`, since the spec path treats every level-4 child as a scenario). Both readers delegate to these. `SHALL`/`MUST` detection uses one predicate.
|
||||
|
||||
Why the existing fence tests still pass: in `markdown-parser.test.ts:106`/`:139` the `SHALL` line is first and the fenced block follows, so skipping fenced lines leaves `text` exactly equal to the `SHALL` line — the asserted value. The breaking case (#312) is the inverse — fence *before* prose — which no test covers.
|
||||
|
||||
### Part B — surface the #498 divergence (INFO, no recognition change)
|
||||
|
||||
`parseDeltaSpec` records the non-canonical level-3 headers it skips *while parsing* the `## ADDED`/`## MODIFIED Requirements` sections, and `validateChangeDeltaSpecs` emits each as an INFO issue. Collecting during the parse (rather than with a separate scanner) guarantees the note describes the reader's real boundaries — a header the reader never saw (e.g. after a fenced `##` line ended the section early) gets no note, and a fenced `###` example line, which the body reader treats as content, is not reported. Under `--strict`, `valid = errors === 0 && warnings === 0` — **INFO is excluded**, so this never changes pass/fail; it only informs. This is the minimal change that makes `validate <change>` stop *silently* passing the #498 input.
|
||||
|
||||
## Why recognition tightening is rejected
|
||||
|
||||
The obvious #498 fix is to make `parseRequirements` recognize only `### Requirement:` headers. It is rejected because **bare `### <statement>` headers are a supported, tested requirement format**, not a convention violation:
|
||||
|
||||
- `test/core/validation.test.ts` builds a spec whose requirements are `### The system SHALL provide secure user authentication` (no `Requirement:` prefix) and asserts `report.valid === true`.
|
||||
- Bare headers also appear as valid requirements in `test/core/converters/json-converter.test.ts`, `test/core/archive.test.ts`, `test/commands/spec.test.ts`, and `test/core/parsers/markdown-parser.test.ts` (`:258`, `:310`, and the fixtures at `:14`/`:22`/`:55`/`:85`).
|
||||
|
||||
Tightening would reclassify all of these as non-requirements, breaking those tests and silently dropping requirements from any real spec that uses the bare style. The cost is not justified by #498, whose harm is a *confusing signal*, not data loss (the archive rebuild already filters to `### Requirement:` blocks, so rebuilt specs are correct regardless). Part B fixes the signal safely. If maintainers later decide to make `### Requirement:` mandatory, that belongs in its own change with a deprecation cycle and fixture migration.
|
||||
|
||||
## Safety: write path is independent of the reader
|
||||
|
||||
`src/core/specs-apply.ts` rebuilds specs during archive from `extractRequirementsSection` + `RequirementBlock.raw` (raw text split on the canonical header). It does not import or call `parseSpec`/`parseRequirements` and never reads `req.text`. Consequently Part A changes only what is *read/validated/displayed*; archived spec bytes are unchanged. (Note: this means `specs-apply` already uses the canonical `### Requirement:` rule — another reason recognition divergence is a reader-only concern.)
|
||||
|
||||
## Read-only blast radius (no write path)
|
||||
|
||||
Consumers of `parseSpec`/`req.text`: `view.ts`/`list.ts` (requirement **counts** — unchanged, since recognition is unchanged), `json-converter.ts` (JSON `text` — now the full body), `spec.ts` (display), `change-parser.ts:96` (delta descriptions `Add requirement: ${req.text}` — may span lines), and the `MAX_REQUIREMENT_TEXT_LENGTH` INFO (non-blocking). None affect archived content or pass/fail of valid specs.
|
||||
|
||||
## Edge cases for tests
|
||||
|
||||
- Single-line requirement unchanged (text and count byte-for-byte).
|
||||
- Metadata-only body still flags missing `SHALL`/`MUST`.
|
||||
- Fenced `#### Scenario:` / `#`-comment lines do not corrupt text or inflate scenario count.
|
||||
- LF/CRLF/CR via `normalizeContent`; `~~~`/length-≥3/leading-whitespace fences via existing `buildCodeFenceMask`.
|
||||
- INFO note appears for a stray delta header but does not change `valid` (including `--strict`).
|
||||
|
||||
## Known remaining divergences
|
||||
|
||||
Unification closes the reproduced defects; these divergences remain and are accepted:
|
||||
|
||||
- **Empty scenarios** — a `#### Scenario:` header with no body counts on the delta path (`countScenarios` counts headers) but not on the spec path (`parseScenarios` keeps only scenarios with content), so `validate <change>` passes what `validate <spec>`/`archive` rejects.
|
||||
- **Recognition** — bare `### <statement>` headers are requirements on the spec path but skipped on the delta path. Deliberate (see "Why recognition tightening is rejected"); the Part B INFO note surfaces it instead of unifying it.
|
||||
- **No-space `###Requirement:` headers** — `REQUIREMENT_HEADER_REGEX` (`\s*` after `###`) accepts them on the delta and write paths, but `MarkdownParser.parseSections` requires whitespace (matching GFM, which does not treat `###Requirement:` as a heading). So a no-space requirement validates as a change with zero INFO (the reader accepts it, so the skip note never fires), syncs into the main spec as-is, and the synced spec then fails `validate <spec>` — the same shape as #498. Pre-existing (both regexes unchanged from `main`) and accepted here: the no-space form is a tested normalization case (`requirement-blocks.test.ts`), and tightening the shared regex would change write-path recognition. Closing it should be a separate compatibility change — deprecate no-space headers with an INFO/WARN first, or broaden the skipped-header collection to any `^###` line before tightening recognition.
|
||||
- **Delta section/block splitting is not fence-aware** — `splitTopLevelSections` and `parseRequirementBlocksFromSection` treat a fenced `## ...` line as a section boundary and a fenced `### Requirement:` line as a new block, while the spec path fence-masks its sectioning. The skipped-header INFO is collected during the actual parse precisely so it reflects these boundaries instead of describing different ones.
|
||||
|
||||
## Prior art
|
||||
|
||||
`findMainSpecStructureIssues` (`spec-structure.ts`) already flags a `### Requirement:` header *outside* the `## Requirements` section and delta headers inside a main spec. The Part B INFO note is complementary: it flags non-`Requirement:` headers *inside* a delta Requirements section, which that function does not cover.
|
||||
|
||||
## Out of scope: #559
|
||||
|
||||
Deferred — transcript shows an unqualified `changes/<id>/...` path (missing `openspec/` prefix), not a demonstrated folder-vs-title mismatch.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user