Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/add-skill-cli-auto-approval.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions .changeset/add-trae-command-adapter.md
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions .changeset/fix-archive-exit-code.md
Original file line number Diff line number Diff line change
@@ -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).
16 changes: 16 additions & 0 deletions .changeset/spec-parser-reading-fidelity.md
Original file line number Diff line number Diff line change
@@ -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).
11 changes: 0 additions & 11 deletions .changeset/stores-beta-config-parse.md

This file was deleted.

76 changes: 76 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ CLAUDE.md

# Pnpm
.pnpm-store/
/package-lock.json
result

# OpenCode
Expand All @@ -164,3 +165,6 @@ opencode.json
# Codewalla telemetry identity (project-local fallback — primary is ~/.config/openspec/)
.openspec/telemetry-identity
.windsurf/...

# Trae
.trae/
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@

**Breaking:** Existing changes with `tasks.md` but no `plan.md` are blocked until a plan is added.

## 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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ AI coding assistants are powerful but unpredictable when requirements live only
- **Work fluidly** — update any artifact anytime with `/opsx:modify`; no rigid phase gates
- **Jira-native workflow** — propose from ticket keys, enrich from Jira during apply, traceability in proposal Impact
- **MCP-powered** — Atlassian, Context7, and browser MCPs built into generated skills
- **Use your tools** — works with 20+ AI assistants via slash commands
- **Use your tools** — works with 30+ AI assistants via slash commands

## Updating OpenSpec

Expand Down
7 changes: 7 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ That second one matters more than it looks. OpenSpec has two halves: a command l

**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.
Expand Down Expand Up @@ -52,6 +56,9 @@ That second one matters more than it looks. OpenSpec has two halves: a command l
| [Workflows](workflows.md) | Common patterns and when to reach for each command |
| [MCP Setup](mcp-setup.md) | Atlassian, Context7, and browser MCP configuration |
| [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 |
Expand Down
4 changes: 2 additions & 2 deletions docs/agent-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ setup/register: `{ "store": {id, root, metadata_path?}, "registry": {path, regis
`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_root_missing`, `openspec_config_missing`, `openspec_specs_missing`, `openspec_changes_missing`, `openspec_archive_missing`, plus `_not_directory` variants of each.
`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_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_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).
Expand Down
16 changes: 14 additions & 2 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ openspec init [path] [options]

`--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`, `opencode`, `pi`, `qoder`, `qwen`, `roocode`, `trae`, `windsurf`
**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.

Expand Down Expand Up @@ -215,7 +215,12 @@ openspec store setup team-context --path ~/openspec/team-context --no-init-git -

### `openspec store register`

Register an existing local store folder.
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]
Expand Down Expand Up @@ -620,6 +625,13 @@ Create a change directory and optional checked-in metadata in the resolved OpenS
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 |
Expand Down
53 changes: 52 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For workflow patterns and when to use each command, see [Workflows](workflows.md
| `/opsx:explore` | Think through ideas before committing to a change |
| `/opsx:modify` | Revise planning artifacts before implementation (pre-apply) |
| `/opsx:apply` | Implement tasks from the change |
| `/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 |

Expand Down Expand Up @@ -364,6 +365,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.
Expand Down Expand Up @@ -667,8 +717,9 @@ Different AI tools use slightly different command syntax. Use the format that ma
| 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 | Skill-based invocations such as `/openspec-propose`, `/openspec-apply-change` (no generated `opsx-*` command files) |
| Trae | `/opsx-propose`, `/opsx-apply` |

The intent is the same across tools, but how commands are surfaced can differ by integration.

Expand Down
Loading