mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-04 06:18:24 +08:00
Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b415cb9b | ||
|
|
e91568deb9 | ||
|
|
fc0d798f93 | ||
|
|
d155126235 | ||
|
|
943e0d4102 | ||
|
|
12a7224dc6 | ||
|
|
c773ef6feb | ||
|
|
0bfe1d4426 | ||
|
|
0e6f42c81c | ||
|
|
0cc9d9025a | ||
|
|
3261ccf6dc | ||
|
|
26ed336a16 | ||
|
|
847aa81c0f | ||
|
|
39bebefcc4 | ||
|
|
cf8b6212c8 | ||
|
|
c157483685 | ||
|
|
f90c7c3354 | ||
|
|
9381bd3b24 | ||
|
|
ae83b4e16d | ||
|
|
d48528134b | ||
|
|
54bd3f1ccd | ||
|
|
675e870bf1 | ||
|
|
07eaf7b691 | ||
|
|
153721d14a | ||
|
|
70c2e17525 | ||
|
|
e2c333e493 | ||
|
|
e137dd3981 | ||
|
|
2beb8e77e8 | ||
|
|
3b16b13613 | ||
|
|
c4cfdc7c49 | ||
|
|
e0736807b4 | ||
|
|
fdb05a723e |
@@ -1,146 +0,0 @@
|
||||
name: Polish Release Notes
|
||||
|
||||
# Uses Claude to transform raw changelog into polished release notes.
|
||||
# Triggered automatically by release-prepare after publishing, or manually.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag_name:
|
||||
description: 'Release tag to polish (e.g., v0.18.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
TAG_NAME: ${{ inputs.tag_name }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
polish:
|
||||
# Only run on the main repo, not forks
|
||||
if: github.repository == 'Fission-AI/OpenSpec'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get current release body
|
||||
id: get-release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release view "${{ env.TAG_NAME }}" --json body -q '.body' > current-notes.md
|
||||
echo "Fetched release notes for ${{ env.TAG_NAME }}"
|
||||
|
||||
- name: Transform release notes with Claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
id: claude
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
claude_args: "--allowedTools Write,Read"
|
||||
prompt: |
|
||||
Transform the changelog in `current-notes.md` into release notes for OpenSpec ${{ env.TAG_NAME }}.
|
||||
|
||||
## Voice
|
||||
|
||||
OpenSpec is a developer tool. Write like you're talking to a peer:
|
||||
- Direct and practical, not marketing copy
|
||||
- Focus on what changed and why it matters
|
||||
- Skip the hype, keep it real
|
||||
|
||||
## Output
|
||||
|
||||
Create two files:
|
||||
|
||||
### 1. `release-title.txt`
|
||||
|
||||
A short title in this format:
|
||||
```
|
||||
${{ env.TAG_NAME }} - [1-4 words describing the release]
|
||||
```
|
||||
|
||||
Examples:
|
||||
- `v0.18.0 - OPSX Experimental Workflow`
|
||||
- `v0.16.0 - Antigravity, iFlow Support`
|
||||
- `v0.15.0 - Gemini CLI, RooCode`
|
||||
|
||||
Rules for title:
|
||||
- Lead with the most notable addition
|
||||
- 1-4 words after the dash, no fluff
|
||||
- If multiple features, comma-separate the top 2
|
||||
- For bugfix-only releases, use something like `v0.17.2 - Pre-commit Hook Fix`
|
||||
|
||||
### 2. `polished-notes.md`
|
||||
|
||||
```markdown
|
||||
## What's New in ${{ env.TAG_NAME }}
|
||||
|
||||
[One sentence: what's the theme of this release?]
|
||||
|
||||
### New
|
||||
|
||||
- **Feature name** - What it does and why you'd use it
|
||||
|
||||
### Improved
|
||||
|
||||
- **Area** - What got better
|
||||
|
||||
### Fixed
|
||||
|
||||
- What was broken, now works
|
||||
```
|
||||
|
||||
Omit empty sections.
|
||||
|
||||
## Rules
|
||||
|
||||
1. Write for developers using OpenSpec with AI coding assistants
|
||||
2. Remove commit hashes (like `eb152eb:`), PR numbers, and changesets wrappers (`### Minor Changes`)
|
||||
3. Lead with what users can do, not implementation details
|
||||
4. One to two sentences per item, max
|
||||
5. Use **bold** for feature/area names
|
||||
6. Skip internal changes (CI, refactors, tests) unless they affect users
|
||||
7. If the input is already well-formatted, just clean up structure and remove noise
|
||||
|
||||
## Example
|
||||
|
||||
Before:
|
||||
```
|
||||
### Minor Changes
|
||||
- 8dfd824: Add OPSX experimental workflow commands and enhanced artifact system
|
||||
**New Commands:**
|
||||
- `/opsx:ff` - Fast-forward through artifact creation
|
||||
```
|
||||
|
||||
After (polished-notes.md):
|
||||
```
|
||||
### New
|
||||
|
||||
- **Fast-forward mode** - Generate all planning artifacts at once with `/opsx:ff`. Useful when you already know what you're building.
|
||||
```
|
||||
|
||||
After (release-title.txt):
|
||||
```
|
||||
v0.18.0 - OPSX Experimental Workflow
|
||||
```
|
||||
|
||||
Write both files. No other output.
|
||||
|
||||
- name: Update release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG="${{ env.TAG_NAME }}"
|
||||
|
||||
if [ -f "polished-notes.md" ] && [ -f "release-title.txt" ]; then
|
||||
TITLE=$(cat release-title.txt)
|
||||
gh release edit "$TAG" --title "$TITLE" --notes-file polished-notes.md
|
||||
echo "Updated: $TITLE"
|
||||
elif [ -f "polished-notes.md" ]; then
|
||||
gh release edit "$TAG" --notes-file polished-notes.md
|
||||
echo "Updated notes (title unchanged)"
|
||||
else
|
||||
echo "No changes generated, keeping original"
|
||||
fi
|
||||
@@ -58,14 +58,3 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
# npm authentication handled via OIDC trusted publishing (no token needed)
|
||||
|
||||
# Trigger release notes polishing after a release is published
|
||||
- name: Polish release notes
|
||||
if: steps.changesets.outputs.published == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
# Get version from package.json (just bumped by changesets)
|
||||
TAG="v$(jq -r .version package.json)"
|
||||
echo "Triggering polish workflow for $TAG"
|
||||
gh workflow run polish-release-notes.yml -f tag_name="$TAG"
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<!-- OPENSPEC:START -->
|
||||
# OpenSpec Instructions
|
||||
|
||||
These instructions are for AI assistants working in this project.
|
||||
|
||||
Always open `@/openspec/AGENTS.md` when the request:
|
||||
- Mentions planning or proposals (words like proposal, spec, change, plan)
|
||||
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
|
||||
- Sounds ambiguous and you need the authoritative spec before coding
|
||||
|
||||
Use `@/openspec/AGENTS.md` to learn:
|
||||
- How to create and apply change proposals
|
||||
- Spec format and conventions
|
||||
- Project structure and guidelines
|
||||
|
||||
Keep this managed block so 'openspec update' can refresh the instructions.
|
||||
|
||||
<!-- OPENSPEC:END -->
|
||||
|
||||
+115
@@ -1,5 +1,120 @@
|
||||
# @fission-ai/openspec
|
||||
|
||||
## 1.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#596](https://github.com/Fission-AI/OpenSpec/pull/596) [`e91568d`](https://github.com/Fission-AI/OpenSpec/commit/e91568deb948073f3e9d9bb2d2ab5bf8080d6cf4) Thanks [@TabishB](https://github.com/TabishB)! - ### Bug Fixes
|
||||
|
||||
- Clarified spec naming convention — Specs should be named after capabilities (`specs/<capability>/spec.md`), not changes
|
||||
- Fixed task checkbox format guidance — Tasks now clearly require `- [ ]` checkbox format for apply phase tracking
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#587](https://github.com/Fission-AI/OpenSpec/pull/587) [`943e0d4`](https://github.com/Fission-AI/OpenSpec/commit/943e0d41026d034de66b9442d1276c01b293eb2b) Thanks [@TabishB](https://github.com/TabishB)! - ### Bug Fixes
|
||||
|
||||
- Fixed incorrect archive path in onboarding documentation — the template now shows the correct path `openspec/changes/archive/YYYY-MM-DD-<name>/` instead of the incorrect `openspec/archive/YYYY-MM-DD--<name>/`
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- [#578](https://github.com/Fission-AI/OpenSpec/pull/578) [`0cc9d90`](https://github.com/Fission-AI/OpenSpec/commit/0cc9d9025af367faa1688a7b2606a2549053cd3f) Thanks [@TabishB](https://github.com/TabishB)! - ## OpenSpec 1.0 — The OPSX Release
|
||||
|
||||
The workflow has been rebuilt from the ground up. OPSX replaces the old phase-locked `/openspec:*` commands with an action-based system where AI understands what artifacts exist, what's ready to create, and what each action unlocks.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **Old commands removed** — `/openspec:proposal`, `/openspec:apply`, and `/openspec:archive` no longer exist
|
||||
- **Config files removed** — Tool-specific instruction files (`CLAUDE.md`, `.cursorrules`, `AGENTS.md`, `project.md`) are no longer generated
|
||||
- **Migration** — Run `openspec init` to upgrade. Legacy artifacts are detected and cleaned up with confirmation.
|
||||
|
||||
### From Static Prompts to Dynamic Instructions
|
||||
|
||||
**Before:** AI received the same static instructions every time, regardless of project state.
|
||||
|
||||
**Now:** Instructions are dynamically assembled from three layers:
|
||||
|
||||
1. **Context** — Project background from `config.yaml` (tech stack, conventions)
|
||||
2. **Rules** — Artifact-specific constraints (e.g., "propose spike tasks for unknowns")
|
||||
3. **Template** — The actual structure for the output file
|
||||
|
||||
AI queries the CLI for real-time state: which artifacts exist, what's ready to create, what dependencies are satisfied, and what each action unlocks.
|
||||
|
||||
### From Phase-Locked to Action-Based
|
||||
|
||||
**Before:** Linear workflow — proposal → apply → archive. Couldn't easily go back or iterate.
|
||||
|
||||
**Now:** Flexible actions on a change. Edit any artifact anytime. The artifact graph tracks state automatically.
|
||||
|
||||
| Command | What it does |
|
||||
| -------------------- | ---------------------------------------------------- |
|
||||
| `/opsx:explore` | Think through ideas before committing to a change |
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create one artifact at a time (step-through) |
|
||||
| `/opsx:ff` | Create all planning artifacts at once (fast-forward) |
|
||||
| `/opsx:apply` | Implement tasks |
|
||||
| `/opsx:verify` | Validate implementation matches artifacts |
|
||||
| `/opsx:sync` | Sync delta specs to main specs |
|
||||
| `/opsx:archive` | Archive completed change |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes with conflict detection |
|
||||
| `/opsx:onboard` | Guided 15-minute walkthrough of complete workflow |
|
||||
|
||||
### From Text Merging to Semantic Spec Syncing
|
||||
|
||||
**Before:** Spec updates required manual merging or wholesale file replacement.
|
||||
|
||||
**Now:** Delta specs use semantic markers that AI understands:
|
||||
|
||||
- `## ADDED Requirements` — New requirements to add
|
||||
- `## MODIFIED Requirements` — Partial updates (add scenario without copying existing ones)
|
||||
- `## REMOVED Requirements` — Delete with reason and migration notes
|
||||
- `## RENAMED Requirements` — Rename preserving content
|
||||
|
||||
Archive parses these at the requirement level, not brittle header matching.
|
||||
|
||||
### From Scattered Files to Agent Skills
|
||||
|
||||
**Before:** 8+ config files at project root + slash commands scattered across 21 tool-specific locations with different formats.
|
||||
|
||||
**Now:** Single `.claude/skills/` directory with YAML-fronted markdown files. Auto-detected by Claude Code, Cursor, Windsurf. Cross-editor compatible.
|
||||
|
||||
### New Features
|
||||
|
||||
- **Onboarding skill** — `/opsx:onboard` walks new users through their first complete change with codebase-aware task suggestions and step-by-step narration (11 phases, ~15 minutes)
|
||||
|
||||
- **21 AI tools supported** — Claude Code, Cursor, Windsurf, Continue, Gemini CLI, GitHub Copilot, Amazon Q, Cline, RooCode, Kilo Code, Auggie, CodeBuddy, Qoder, Qwen, CoStrict, Crush, Factory, OpenCode, Antigravity, iFlow, and Codex
|
||||
|
||||
- **Interactive setup** — `openspec init` shows animated welcome screen and searchable multi-select for choosing tools. Pre-selects already-configured tools for easy refresh.
|
||||
|
||||
- **Customizable schemas** — Define custom artifact workflows in `openspec/schemas/` without touching package code. Teams can share workflows via version control.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed Claude Code YAML parsing failure when command names contained colons
|
||||
- Fixed task file parsing to handle trailing whitespace on checkbox lines
|
||||
- Fixed JSON instruction output to separate context/rules from template — AI was copying constraint blocks into artifact files
|
||||
|
||||
### Documentation
|
||||
|
||||
- New getting-started guide, CLI reference, concepts documentation
|
||||
- Removed misleading "edit mid-flight and continue" claims that weren't implemented
|
||||
- Added migration guide for upgrading from pre-OPSX versions
|
||||
|
||||
## 0.23.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#540](https://github.com/Fission-AI/OpenSpec/pull/540) [`c4cfdc7`](https://github.com/Fission-AI/OpenSpec/commit/c4cfdc7c499daef30d8a218f5f59b8d9e5adb754) Thanks [@TabishB](https://github.com/TabishB)! - ### New Features
|
||||
|
||||
- **Bulk archive skill** — Archive multiple completed changes in a single operation with `/opsx:bulk-archive`. Includes batch validation, spec conflict detection, and consolidated confirmation
|
||||
|
||||
### Other
|
||||
|
||||
- **Simplified setup** — Config creation now uses sensible defaults with helpful comments instead of interactive prompts
|
||||
|
||||
## 0.22.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,468 +1,195 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/Fission-AI/OpenSpec">
|
||||
<picture>
|
||||
<source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
|
||||
<source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
|
||||
<img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
|
||||
<source srcset="assets/openspec_bg.png">
|
||||
<img src="assets/openspec_bg.png" alt="OpenSpec logo">
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
</p>
|
||||
<p align="center">Spec-driven development for AI coding assistants.</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
|
||||
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
|
||||
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
|
||||
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
|
||||
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
|
||||
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
|
||||
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
|
||||
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
|
||||
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/discord/1411657095639601154?style=flat-square&logo=discord&logoColor=white&label=Discord&suffix=%20online" /></a>
|
||||
</p>
|
||||
|
||||
<details>
|
||||
<summary><strong>The most loved spec framework.</strong></summary>
|
||||
|
||||
[](https://github.com/Fission-AI/OpenSpec/stargazers)
|
||||
[](https://www.npmjs.com/package/@fission-ai/openspec)
|
||||
[](https://github.com/Fission-AI/OpenSpec/graphs/contributors)
|
||||
|
||||
</details>
|
||||
<p></p>
|
||||
Our philosophy:
|
||||
|
||||
```text
|
||||
→ fluid not rigid
|
||||
→ iterative not waterfall
|
||||
→ easy not complex
|
||||
→ built for brownfield not just greenfield
|
||||
→ scalable from personal projects to enterprises
|
||||
```
|
||||
|
||||
> [!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)
|
||||
|
||||
<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 -->
|
||||
|
||||
## See it in action
|
||||
|
||||
```text
|
||||
You: /opsx:new add-dark-mode
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
Ready to create: proposal
|
||||
|
||||
You: /opsx:ff # "fast-forward" - generate all planning docs
|
||||
AI: ✓ proposal.md — why we're doing this, what's changing
|
||||
✓ specs/ — requirements and scenarios
|
||||
✓ design.md — technical approach
|
||||
✓ tasks.md — implementation checklist
|
||||
Ready for implementation!
|
||||
|
||||
You: /opsx:apply
|
||||
AI: Implementing tasks...
|
||||
✓ 1.1 Add theme context provider
|
||||
✓ 1.2 Create toggle component
|
||||
✓ 2.1 Add CSS variables
|
||||
✓ 2.2 Wire up localStorage
|
||||
All tasks complete!
|
||||
|
||||
You: /opsx:archive
|
||||
AI: Archived to openspec/changes/archive/2025-01-23-add-dark-mode/
|
||||
Specs updated. Ready for the next feature.
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><strong>OpenSpec Dashboard</strong></summary>
|
||||
|
||||
<p align="center">
|
||||
<sub>🧪 <strong>New:</strong> <a href="docs/experimental-workflow.md">Experimental Workflow (OPSX)</a> — schema-driven, hackable, fluid. Iterate on workflows without code changes.</sub>
|
||||
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
|
||||
</p>
|
||||
|
||||
# OpenSpec
|
||||
|
||||
OpenSpec aligns humans and AI coding assistants with spec-driven development so you agree on what to build before any code is written. **No API keys required.**
|
||||
|
||||
## Why OpenSpec?
|
||||
|
||||
AI coding assistants are powerful but unpredictable when requirements live in chat history. OpenSpec adds a lightweight specification workflow that locks intent before implementation, giving you deterministic, reviewable outputs.
|
||||
|
||||
Key outcomes:
|
||||
- Human and AI stakeholders agree on specs before work begins.
|
||||
- Structured change folders (proposals, tasks, and spec updates) keep scope explicit and auditable.
|
||||
- Shared visibility into what's proposed, active, or archived.
|
||||
- Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
|
||||
|
||||
## How OpenSpec compares (at a glance)
|
||||
|
||||
- **Lightweight**: simple workflow, no API keys, minimal setup.
|
||||
- **Brownfield-first**: works great beyond 0→1. OpenSpec separates the source of truth from proposals: `openspec/specs/` (current truth) and `openspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
|
||||
- **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
|
||||
- **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). OpenSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
|
||||
|
||||
See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ Draft Change │
|
||||
│ Proposal │
|
||||
└────────┬───────────┘
|
||||
│ share intent with your AI
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Review & Align │
|
||||
│ (edit specs/tasks) │◀──── feedback loop ──────┐
|
||||
└────────┬───────────┘ │
|
||||
│ approved plan │
|
||||
▼ │
|
||||
┌────────────────────┐ │
|
||||
│ Implement Tasks │──────────────────────────┘
|
||||
│ (AI writes code) │
|
||||
└────────┬───────────┘
|
||||
│ ship the change
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Archive & Update │
|
||||
│ Specs (source) │
|
||||
└────────────────────┘
|
||||
|
||||
1. Draft a change proposal that captures the spec updates you want.
|
||||
2. Review the proposal with your AI assistant until everyone agrees.
|
||||
3. Implement tasks that reference the agreed specs.
|
||||
4. Archive the change to merge the approved updates back into the source-of-truth specs.
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Supported AI Tools
|
||||
|
||||
<details>
|
||||
<summary><strong>Native Slash Commands</strong> (click to expand)</summary>
|
||||
|
||||
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
|
||||
|
||||
| Tool | Commands |
|
||||
|------|----------|
|
||||
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
|
||||
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
|
||||
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
|
||||
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
|
||||
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
|
||||
| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
|
||||
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
|
||||
| **Continue** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.continue/prompts/`) |
|
||||
| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai)|
|
||||
| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.crush/commands/openspec/`) |
|
||||
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
||||
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
|
||||
| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.gemini/commands/openspec/`) |
|
||||
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
|
||||
| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.iflow/commands/`) |
|
||||
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
|
||||
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
||||
| **Qoder (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com/cli) |
|
||||
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
|
||||
| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.roo/commands/`) |
|
||||
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
|
||||
|
||||
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
|
||||
## Quick Start
|
||||
|
||||
These tools automatically read workflow instructions from `openspec/AGENTS.md`. Ask them to follow the OpenSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
|
||||
**Requires Node.js 20.19.0 or higher.**
|
||||
|
||||
| Tools |
|
||||
|-------|
|
||||
| Amp • Jules • Others |
|
||||
|
||||
</details>
|
||||
|
||||
### Install & Initialize
|
||||
|
||||
#### Prerequisites
|
||||
- **Node.js >= 20.19.0** - Check your version with `node --version`
|
||||
|
||||
#### Step 1: Install the CLI globally
|
||||
|
||||
**Option A: Using npm**
|
||||
Install OpenSpec globally:
|
||||
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
openspec --version
|
||||
```
|
||||
Then navigate to your project directory and initialize:
|
||||
|
||||
**Option B: Using Nix (NixOS and Nix package manager)**
|
||||
|
||||
Run OpenSpec directly without installation:
|
||||
```bash
|
||||
nix run github:Fission-AI/OpenSpec -- init
|
||||
```
|
||||
|
||||
Or install to your profile:
|
||||
```bash
|
||||
nix profile install github:Fission-AI/OpenSpec
|
||||
```
|
||||
|
||||
Or add to your development environment in `flake.nix`:
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
openspec.url = "github:Fission-AI/OpenSpec";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, openspec, ... }: {
|
||||
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
buildInputs = [ openspec.packages.x86_64-linux.default ];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
openspec --version
|
||||
```
|
||||
|
||||
#### Step 2: Initialize OpenSpec in your project
|
||||
|
||||
Navigate to your project directory:
|
||||
```bash
|
||||
cd my-project
|
||||
```
|
||||
|
||||
Run the initialization:
|
||||
```bash
|
||||
cd your-project
|
||||
openspec init
|
||||
```
|
||||
|
||||
**What happens during initialization:**
|
||||
- You'll be prompted to pick any natively supported AI tools (Claude Code, CodeBuddy, Cursor, OpenCode, Qoder,etc.); other assistants always rely on the shared `AGENTS.md` stub
|
||||
- OpenSpec automatically configures slash commands for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
|
||||
- A new `openspec/` directory structure is created in your project
|
||||
Now tell your AI: `/opsx:new <what-you-want-to-build>`
|
||||
|
||||
**After setup:**
|
||||
- Primary AI tools can trigger `/openspec` workflows without additional configuration
|
||||
- Run `openspec list` to verify the setup and view any active changes
|
||||
- If your coding assistant doesn't surface the new slash commands right away, restart it. Slash commands are loaded at startup,
|
||||
so a fresh launch ensures they appear
|
||||
> [!NOTE]
|
||||
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 20+ tools and growing.
|
||||
>
|
||||
> Also works with pnpm, yarn, bun, and nix. [See installation options](docs/installation.md).
|
||||
|
||||
### Optional: Populate Project Context
|
||||
## Docs
|
||||
|
||||
After `openspec init` completes, you'll receive a suggested prompt to help populate your project context:
|
||||
→ **[Getting Started](docs/getting-started.md)**: first steps<br>
|
||||
→ **[Workflows](docs/workflows.md)**: combos and patterns<br>
|
||||
→ **[Commands](docs/commands.md)**: slash commands & skills<br>
|
||||
→ **[CLI](docs/cli.md)**: terminal reference<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
|
||||
|
||||
```text
|
||||
Populate your project context:
|
||||
"Please read openspec/project.md and help me fill it out with details about my project, tech stack, and conventions"
|
||||
```
|
||||
|
||||
Use `openspec/project.md` to define project-level conventions, standards, architectural patterns, and other guidelines that should be followed across all changes.
|
||||
## Why OpenSpec?
|
||||
|
||||
### Create Your First Change
|
||||
AI coding assistants are powerful but unpredictable when requirements live only in chat history. OpenSpec adds a lightweight spec layer so you agree on what to build before any code is written.
|
||||
|
||||
Here's a real example showing the complete OpenSpec workflow. This works with any AI tool. Those with native slash commands will recognize the shortcuts automatically.
|
||||
- **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
|
||||
|
||||
#### 1. Draft the Proposal
|
||||
Start by asking your AI to create a change proposal:
|
||||
### How we compare
|
||||
|
||||
```text
|
||||
You: Create an OpenSpec change proposal for adding profile search filters by role and team
|
||||
(Shortcut for tools with slash commands: /openspec:proposal Add profile search filters)
|
||||
**vs. [Spec Kit](https://github.com/github/spec-kit)** (GitHub) — Thorough but heavyweight. Rigid phase gates, lots of Markdown, Python setup. OpenSpec is lighter and lets you iterate freely.
|
||||
|
||||
AI: I'll create an OpenSpec change proposal for profile filters.
|
||||
*Scaffolds openspec/changes/add-profile-filters/ with proposal.md, tasks.md, spec deltas.*
|
||||
```
|
||||
**vs. [Kiro](https://kiro.dev)** (AWS) — Powerful but you're locked into their IDE and limited to Claude models. OpenSpec works with the tools you already use.
|
||||
|
||||
#### 2. Verify & Review
|
||||
Check that the change was created correctly and review the proposal:
|
||||
|
||||
```bash
|
||||
$ openspec list # Confirm the change folder exists
|
||||
$ openspec validate add-profile-filters # Validate spec formatting
|
||||
$ openspec show add-profile-filters # Review proposal, tasks, and spec delta
|
||||
```
|
||||
|
||||
#### 3. Refine the Specs
|
||||
Iterate on the specifications until they match your needs:
|
||||
|
||||
```text
|
||||
You: Can you add acceptance criteria for the role and team filters?
|
||||
|
||||
AI: I'll update the spec delta with scenarios for role and team filters.
|
||||
*Edits openspec/changes/add-profile-filters/specs/profile/spec.md and tasks.md.*
|
||||
```
|
||||
|
||||
#### 4. Implement the Change
|
||||
Once specs look good, start implementation:
|
||||
|
||||
```text
|
||||
You: The specs look good. Let's implement this change.
|
||||
(Shortcut for tools with slash commands: /openspec:apply add-profile-filters)
|
||||
|
||||
AI: I'll work through the tasks in the add-profile-filters change.
|
||||
*Implements tasks from openspec/changes/add-profile-filters/tasks.md*
|
||||
*Marks tasks complete: Task 1.1 ✓, Task 1.2 ✓, Task 2.1 ✓...*
|
||||
```
|
||||
|
||||
#### 5. Archive the Completed Change
|
||||
After implementation is complete, archive the change:
|
||||
|
||||
```text
|
||||
AI: All tasks are complete. The implementation is ready.
|
||||
|
||||
You: Please archive the change
|
||||
(Shortcut for tools with slash commands: /openspec:archive add-profile-filters)
|
||||
|
||||
AI: I'll archive the add-profile-filters change.
|
||||
*Runs: openspec archive add-profile-filters --yes*
|
||||
✓ Change archived successfully. Specs updated. Ready for the next feature!
|
||||
```
|
||||
|
||||
Or run the command yourself in terminal:
|
||||
```bash
|
||||
$ openspec archive add-profile-filters --yes # Archive the completed change without prompts
|
||||
```
|
||||
|
||||
**Note:** Tools with native slash commands (Claude Code, CodeBuddy, Cursor, Codex, Qoder, RooCode) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
|
||||
|
||||
## Command Reference
|
||||
|
||||
```bash
|
||||
openspec list # View active change folders
|
||||
openspec view # Interactive dashboard of specs and changes
|
||||
openspec show <change> # Display change details (proposal, tasks, spec updates)
|
||||
openspec validate <change> # Check spec formatting and structure
|
||||
openspec archive <change> [--yes|-y] # Move a completed change into archive/ (non-interactive with --yes)
|
||||
```
|
||||
|
||||
## Example: How AI Creates OpenSpec Files
|
||||
|
||||
When you ask your AI assistant to "add two-factor authentication", it creates:
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── specs/
|
||||
│ └── auth/
|
||||
│ └── spec.md # Current auth spec (if exists)
|
||||
└── changes/
|
||||
└── add-2fa/ # AI creates this entire structure
|
||||
├── proposal.md # Why and what changes
|
||||
├── tasks.md # Implementation checklist
|
||||
├── design.md # Technical decisions (optional)
|
||||
└── specs/
|
||||
└── auth/
|
||||
└── spec.md # Delta showing additions
|
||||
```
|
||||
|
||||
### AI-Generated Spec (created in `openspec/specs/auth/spec.md`):
|
||||
|
||||
```markdown
|
||||
# Auth Specification
|
||||
|
||||
## Purpose
|
||||
Authentication and session management.
|
||||
|
||||
## Requirements
|
||||
### Requirement: User Authentication
|
||||
The system SHALL issue a JWT on successful login.
|
||||
|
||||
#### Scenario: Valid credentials
|
||||
- WHEN a user submits valid credentials
|
||||
- THEN a JWT is returned
|
||||
```
|
||||
|
||||
### AI-Generated Change Delta (created in `openspec/changes/add-2fa/specs/auth/spec.md`):
|
||||
|
||||
```markdown
|
||||
# Delta for Auth
|
||||
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
The system MUST require a second factor during login.
|
||||
|
||||
#### Scenario: OTP required
|
||||
- WHEN a user submits valid credentials
|
||||
- THEN an OTP challenge is required
|
||||
```
|
||||
|
||||
### AI-Generated Tasks (created in `openspec/changes/add-2fa/tasks.md`):
|
||||
|
||||
```markdown
|
||||
## 1. Database Setup
|
||||
- [ ] 1.1 Add OTP secret column to users table
|
||||
- [ ] 1.2 Create OTP verification logs table
|
||||
|
||||
## 2. Backend Implementation
|
||||
- [ ] 2.1 Add OTP generation endpoint
|
||||
- [ ] 2.2 Modify login flow to require OTP
|
||||
- [ ] 2.3 Add OTP verification endpoint
|
||||
|
||||
## 3. Frontend Updates
|
||||
- [ ] 3.1 Create OTP input component
|
||||
- [ ] 3.2 Update login flow UI
|
||||
```
|
||||
|
||||
**Important:** You don't create these files manually. Your AI assistant generates them based on your requirements and the existing codebase.
|
||||
|
||||
## Understanding OpenSpec Files
|
||||
|
||||
### Delta Format
|
||||
|
||||
Deltas are "patches" that show how specs change:
|
||||
|
||||
- **`## ADDED Requirements`** - New capabilities
|
||||
- **`## MODIFIED Requirements`** - Changed behavior (include complete updated text)
|
||||
- **`## REMOVED Requirements`** - Deprecated features
|
||||
|
||||
**Format requirements:**
|
||||
- Use `### Requirement: <name>` for headers
|
||||
- Every requirement needs at least one `#### Scenario:` block
|
||||
- Use SHALL/MUST in requirement text
|
||||
|
||||
## How OpenSpec Compares
|
||||
|
||||
### vs. spec-kit
|
||||
OpenSpec’s two-folder model (`openspec/specs/` for the current truth, `openspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
|
||||
|
||||
### vs. Kiro.dev
|
||||
OpenSpec groups every change for a feature in one folder (`openspec/changes/feature-name/`), making it easy to track related specs, tasks, and designs together. Kiro spreads updates across multiple spec folders, which can make feature tracking harder.
|
||||
|
||||
### vs. No Specs
|
||||
Without specs, AI coding assistants generate code from vague prompts, often missing requirements or adding unwanted features. OpenSpec brings predictability by agreeing on the desired behavior before any code is written.
|
||||
|
||||
## Team Adoption
|
||||
|
||||
1. **Initialize OpenSpec** – Run `openspec init` in your repo.
|
||||
2. **Start with new features** – Ask your AI to capture upcoming work as change proposals.
|
||||
3. **Grow incrementally** – Each change archives into living specs that document your system.
|
||||
4. **Stay flexible** – Different teammates can use Claude Code, CodeBuddy, Cursor, or any AGENTS.md-compatible tool while sharing the same specs.
|
||||
|
||||
Run `openspec update` whenever someone switches tools so your agents pick up the latest instructions and slash-command bindings.
|
||||
**vs. nothing** — AI coding without specs means vague prompts and unpredictable results. OpenSpec brings predictability without the ceremony.
|
||||
|
||||
## Updating OpenSpec
|
||||
|
||||
1. **Upgrade the package**
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
2. **Refresh agent instructions**
|
||||
- Run `openspec update` inside each project to regenerate AI guidance and ensure the latest slash commands are active.
|
||||
**Upgrade the package**
|
||||
|
||||
## Experimental Features
|
||||
|
||||
<details>
|
||||
<summary><strong>🧪 OPSX: Fluid, Iterative Workflow</strong> (Claude Code only)</summary>
|
||||
|
||||
**Why this exists:**
|
||||
- Standard workflow is locked down — you can't tweak instructions or customize
|
||||
- When AI output is bad, you can't improve the prompts yourself
|
||||
- Same workflow for everyone, no way to match how your team works
|
||||
|
||||
**What's different:**
|
||||
- **Hackable** — edit templates and schemas yourself, test immediately, no rebuild
|
||||
- **Granular** — each artifact has its own instructions, test and tweak individually
|
||||
- **Customizable** — define your own workflows, artifacts, and dependencies
|
||||
- **Fluid** — no phase gates, update any artifact anytime
|
||||
|
||||
```
|
||||
You can always go back:
|
||||
|
||||
proposal ──→ specs ──→ design ──→ tasks ──→ implement
|
||||
▲ ▲ ▲ │
|
||||
└───────────┴──────────┴────────────────────┘
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create the next artifact (based on what's ready) |
|
||||
| `/opsx:ff` | Fast-forward (all planning artifacts at once) |
|
||||
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
|
||||
| `/opsx:archive` | Archive when done |
|
||||
**Refresh agent instructions**
|
||||
|
||||
**Setup:** `openspec artifact-experimental-setup`
|
||||
Run this inside each project to regenerate AI guidance and ensure the latest slash commands are active:
|
||||
|
||||
[Full documentation →](docs/experimental-workflow.md)
|
||||
```bash
|
||||
openspec update
|
||||
```
|
||||
|
||||
</details>
|
||||
## Usage Notes
|
||||
|
||||
<details>
|
||||
<summary><strong>Telemetry</strong> – OpenSpec collects anonymous usage stats (opt-out: <code>OPENSPEC_TELEMETRY=0</code>)</summary>
|
||||
**Model selection**: OpenSpec works best with high-reasoning models. We recommend Opus 4.5 and GPT 5.2 for both planning and implementation.
|
||||
|
||||
We collect only command names and version to understand usage patterns. No arguments, paths, content, or PII. Automatically disabled in CI.
|
||||
|
||||
**Opt-out:** `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`
|
||||
|
||||
</details>
|
||||
**Context hygiene**: OpenSpec benefits from a clean context window. Clear your context before starting implementation and maintain good context hygiene throughout your session.
|
||||
|
||||
## Contributing
|
||||
|
||||
**Small fixes** — Bug fixes, typo corrections, and minor improvements can be submitted directly as PRs.
|
||||
|
||||
**Larger changes** — For new features, significant refactors, or architectural changes, please submit an OpenSpec change proposal first so we can align on intent and goals before implementation begins.
|
||||
|
||||
When writing proposals, keep the OpenSpec philosophy in mind: we serve a wide variety of users across different coding agents, models, and use cases. Changes should work well for everyone.
|
||||
|
||||
**AI-generated code is welcome** — as long as it's been tested and verified. PRs containing AI-generated code should mention the coding agent and model used (e.g., "Generated with Claude Code using claude-opus-4-5-20251101").
|
||||
|
||||
### Development
|
||||
|
||||
- Install dependencies: `pnpm install`
|
||||
- Build: `pnpm run build`
|
||||
- Test: `pnpm test`
|
||||
- Develop CLI locally: `pnpm run dev` or `pnpm run dev:cli`
|
||||
- Conventional commits (one-line): `type(scope): subject`
|
||||
|
||||
## Other
|
||||
|
||||
<details>
|
||||
<summary><strong>Telemetry</strong></summary>
|
||||
|
||||
OpenSpec collects anonymous usage stats.
|
||||
|
||||
We collect only command names and version to understand usage patterns. No arguments, paths, content, or PII. Automatically disabled in CI.
|
||||
|
||||
**Opt-out:** `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Maintainers & Advisors</strong></summary>
|
||||
|
||||
@@ -470,6 +197,8 @@ See [MAINTAINERS.md](MAINTAINERS.md) for the list of core maintainers and adviso
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
+475
@@ -0,0 +1,475 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/Fission-AI/OpenSpec">
|
||||
<picture>
|
||||
<source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
|
||||
<source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
|
||||
<img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
</p>
|
||||
<p align="center">Spec-driven development for AI coding assistants.</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
|
||||
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
|
||||
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
|
||||
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
|
||||
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
|
||||
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
||||
<p align="center">
|
||||
<sub>🧪 <strong>New:</strong> <a href="docs/opsx.md">OPSX Workflow</a> — schema-driven, hackable, fluid. Iterate on workflows without code changes.</sub>
|
||||
</p>
|
||||
|
||||
# OpenSpec
|
||||
|
||||
OpenSpec aligns humans and AI coding assistants with spec-driven development so you agree on what to build before any code is written. **No API keys required.**
|
||||
|
||||
## Why OpenSpec?
|
||||
|
||||
AI coding assistants are powerful but unpredictable when requirements live in chat history. OpenSpec adds a lightweight specification workflow that locks intent before implementation, giving you deterministic, reviewable outputs.
|
||||
|
||||
Key outcomes:
|
||||
- Human and AI stakeholders agree on specs before work begins.
|
||||
- Structured change folders (proposals, tasks, and spec updates) keep scope explicit and auditable.
|
||||
- Shared visibility into what's proposed, active, or archived.
|
||||
- Works with the AI tools you already use: custom slash commands where supported, context rules everywhere else.
|
||||
|
||||
## How OpenSpec compares (at a glance)
|
||||
|
||||
- **Lightweight**: simple workflow, no API keys, minimal setup.
|
||||
- **Brownfield-first**: works great beyond 0→1. OpenSpec separates the source of truth from proposals: `openspec/specs/` (current truth) and `openspec/changes/` (proposed updates). This keeps diffs explicit and manageable across features.
|
||||
- **Change tracking**: proposals, tasks, and spec deltas live together; archiving merges the approved updates back into specs.
|
||||
- **Compared to spec-kit & Kiro**: those shine for brand-new features (0→1). OpenSpec also excels when modifying existing behavior (1→n), especially when updates span multiple specs.
|
||||
|
||||
See the full comparison in [How OpenSpec Compares](#how-openspec-compares).
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ Draft Change │
|
||||
│ Proposal │
|
||||
└────────┬───────────┘
|
||||
│ share intent with your AI
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Review & Align │
|
||||
│ (edit specs/tasks) │◀──── feedback loop ──────┐
|
||||
└────────┬───────────┘ │
|
||||
│ approved plan │
|
||||
▼ │
|
||||
┌────────────────────┐ │
|
||||
│ Implement Tasks │──────────────────────────┘
|
||||
│ (AI writes code) │
|
||||
└────────┬───────────┘
|
||||
│ ship the change
|
||||
▼
|
||||
┌────────────────────┐
|
||||
│ Archive & Update │
|
||||
│ Specs (source) │
|
||||
└────────────────────┘
|
||||
|
||||
1. Draft a change proposal that captures the spec updates you want.
|
||||
2. Review the proposal with your AI assistant until everyone agrees.
|
||||
3. Implement tasks that reference the agreed specs.
|
||||
4. Archive the change to merge the approved updates back into the source-of-truth specs.
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Supported AI Tools
|
||||
|
||||
<details>
|
||||
<summary><strong>Native Slash Commands</strong> (click to expand)</summary>
|
||||
|
||||
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
|
||||
|
||||
| Tool | Commands |
|
||||
|------|----------|
|
||||
| **Amazon Q Developer** | `@openspec-proposal`, `@openspec-apply`, `@openspec-archive` (`.amazonq/prompts/`) |
|
||||
| **Antigravity** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.agent/workflows/`) |
|
||||
| **Auggie (Augment CLI)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.augment/commands/`) |
|
||||
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
|
||||
| **Cline** | Workflows in `.clinerules/workflows/` directory (`.clinerules/workflows/openspec-*.md`) |
|
||||
| **CodeBuddy Code (CLI)** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.codebuddy/commands/`) — see [docs](https://www.codebuddy.ai/cli) |
|
||||
| **Codex** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (global: `~/.codex/prompts`, auto-installed) |
|
||||
| **Continue** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.continue/prompts/`) |
|
||||
| **CoStrict** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.cospec/openspec/commands/`) — see [docs](https://costrict.ai)|
|
||||
| **Crush** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.crush/commands/openspec/`) |
|
||||
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
||||
| **Factory Droid** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.factory/commands/`) |
|
||||
| **Gemini CLI** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.gemini/commands/openspec/`) |
|
||||
| **GitHub Copilot** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.github/prompts/`) |
|
||||
| **iFlow (iflow-cli)** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.iflow/commands/`) |
|
||||
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/workflows/`) |
|
||||
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
|
||||
| **Qoder** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` (`.qoder/commands/openspec/`) — see [docs](https://qoder.com) |
|
||||
| **Qwen Code** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.qwen/commands/`) |
|
||||
| **RooCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.roo/commands/`) |
|
||||
| **Windsurf** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` (`.windsurf/workflows/`) |
|
||||
|
||||
Kilo Code discovers team workflows automatically. Save the generated files under `.kilocode/workflows/` and trigger them from the command palette with `/openspec-proposal.md`, `/openspec-apply.md`, or `/openspec-archive.md`.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>AGENTS.md Compatible</strong> (click to expand)</summary>
|
||||
|
||||
These tools automatically read workflow instructions from `openspec/AGENTS.md`. Ask them to follow the OpenSpec workflow if they need a reminder. Learn more about the [AGENTS.md convention](https://agents.md/).
|
||||
|
||||
| Tools |
|
||||
|-------|
|
||||
| Amp • Jules • Others |
|
||||
|
||||
</details>
|
||||
|
||||
### Install & Initialize
|
||||
|
||||
#### Prerequisites
|
||||
- **Node.js >= 20.19.0** - Check your version with `node --version`
|
||||
|
||||
#### Step 1: Install the CLI globally
|
||||
|
||||
**Option A: Using npm**
|
||||
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
openspec --version
|
||||
```
|
||||
|
||||
**Option B: Using Nix (NixOS and Nix package manager)**
|
||||
|
||||
Run OpenSpec directly without installation:
|
||||
```bash
|
||||
nix run github:Fission-AI/OpenSpec -- init
|
||||
```
|
||||
|
||||
Or install to your profile:
|
||||
```bash
|
||||
nix profile install github:Fission-AI/OpenSpec
|
||||
```
|
||||
|
||||
Or add to your development environment in `flake.nix`:
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
openspec.url = "github:Fission-AI/OpenSpec";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, openspec, ... }: {
|
||||
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
buildInputs = [ openspec.packages.x86_64-linux.default ];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
openspec --version
|
||||
```
|
||||
|
||||
#### Step 2: Initialize OpenSpec in your project
|
||||
|
||||
Navigate to your project directory:
|
||||
```bash
|
||||
cd my-project
|
||||
```
|
||||
|
||||
Run the initialization:
|
||||
```bash
|
||||
openspec init
|
||||
```
|
||||
|
||||
**What happens during initialization:**
|
||||
- You'll be prompted to pick any natively supported AI tools (Claude Code, CodeBuddy, Cursor, OpenCode, Qoder,etc.); other assistants always rely on the shared `AGENTS.md` stub
|
||||
- OpenSpec automatically configures slash commands for the tools you choose and always writes a managed `AGENTS.md` hand-off at the project root
|
||||
- A new `openspec/` directory structure is created in your project
|
||||
|
||||
**After setup:**
|
||||
- Primary AI tools can trigger `/openspec` workflows without additional configuration
|
||||
- Run `openspec list` to verify the setup and view any active changes
|
||||
- If your coding assistant doesn't surface the new slash commands right away, restart it. Slash commands are loaded at startup,
|
||||
so a fresh launch ensures they appear
|
||||
|
||||
### Optional: Populate Project Context
|
||||
|
||||
After `openspec init` completes, you'll receive a suggested prompt to help populate your project context:
|
||||
|
||||
```text
|
||||
Populate your project context:
|
||||
"Please read openspec/project.md and help me fill it out with details about my project, tech stack, and conventions"
|
||||
```
|
||||
|
||||
Use `openspec/project.md` to define project-level conventions, standards, architectural patterns, and other guidelines that should be followed across all changes.
|
||||
|
||||
### Create Your First Change
|
||||
|
||||
Here's a real example showing the complete OpenSpec workflow. This works with any AI tool. Those with native slash commands will recognize the shortcuts automatically.
|
||||
|
||||
#### 1. Draft the Proposal
|
||||
Start by asking your AI to create a change proposal:
|
||||
|
||||
```text
|
||||
You: Create an OpenSpec change proposal for adding profile search filters by role and team
|
||||
(Shortcut for tools with slash commands: /openspec:proposal Add profile search filters)
|
||||
|
||||
AI: I'll create an OpenSpec change proposal for profile filters.
|
||||
*Scaffolds openspec/changes/add-profile-filters/ with proposal.md, tasks.md, spec deltas.*
|
||||
```
|
||||
|
||||
#### 2. Verify & Review
|
||||
Check that the change was created correctly and review the proposal:
|
||||
|
||||
```bash
|
||||
$ openspec list # Confirm the change folder exists
|
||||
$ openspec validate add-profile-filters # Validate spec formatting
|
||||
$ openspec show add-profile-filters # Review proposal, tasks, and spec delta
|
||||
```
|
||||
|
||||
#### 3. Refine the Specs
|
||||
Iterate on the specifications until they match your needs:
|
||||
|
||||
```text
|
||||
You: Can you add acceptance criteria for the role and team filters?
|
||||
|
||||
AI: I'll update the spec delta with scenarios for role and team filters.
|
||||
*Edits openspec/changes/add-profile-filters/specs/profile/spec.md and tasks.md.*
|
||||
```
|
||||
|
||||
#### 4. Implement the Change
|
||||
Once specs look good, start implementation:
|
||||
|
||||
```text
|
||||
You: The specs look good. Let's implement this change.
|
||||
(Shortcut for tools with slash commands: /openspec:apply add-profile-filters)
|
||||
|
||||
AI: I'll work through the tasks in the add-profile-filters change.
|
||||
*Implements tasks from openspec/changes/add-profile-filters/tasks.md*
|
||||
*Marks tasks complete: Task 1.1 ✓, Task 1.2 ✓, Task 2.1 ✓...*
|
||||
```
|
||||
|
||||
#### 5. Archive the Completed Change
|
||||
After implementation is complete, archive the change:
|
||||
|
||||
```text
|
||||
AI: All tasks are complete. The implementation is ready.
|
||||
|
||||
You: Please archive the change
|
||||
(Shortcut for tools with slash commands: /openspec:archive add-profile-filters)
|
||||
|
||||
AI: I'll archive the add-profile-filters change.
|
||||
*Runs: openspec archive add-profile-filters --yes*
|
||||
✓ Change archived successfully. Specs updated. Ready for the next feature!
|
||||
```
|
||||
|
||||
Or run the command yourself in terminal:
|
||||
```bash
|
||||
$ openspec archive add-profile-filters --yes # Archive the completed change without prompts
|
||||
```
|
||||
|
||||
**Note:** Tools with native slash commands (Claude Code, CodeBuddy, Cursor, Codex, Qoder, RooCode) can use the shortcuts shown. All other tools work with natural language requests to "create an OpenSpec proposal", "apply the OpenSpec change", or "archive the change".
|
||||
|
||||
## Command Reference
|
||||
|
||||
```bash
|
||||
openspec list # View active change folders
|
||||
openspec view # Interactive dashboard of specs and changes
|
||||
openspec show <change> # Display change details (proposal, tasks, spec updates)
|
||||
openspec validate <change> # Check spec formatting and structure
|
||||
openspec archive <change> [--yes|-y] # Move a completed change into archive/ (non-interactive with --yes)
|
||||
```
|
||||
|
||||
## Example: How AI Creates OpenSpec Files
|
||||
|
||||
When you ask your AI assistant to "add two-factor authentication", it creates:
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── specs/
|
||||
│ └── auth/
|
||||
│ └── spec.md # Current auth spec (if exists)
|
||||
└── changes/
|
||||
└── add-2fa/ # AI creates this entire structure
|
||||
├── proposal.md # Why and what changes
|
||||
├── tasks.md # Implementation checklist
|
||||
├── design.md # Technical decisions (optional)
|
||||
└── specs/
|
||||
└── auth/
|
||||
└── spec.md # Delta showing additions
|
||||
```
|
||||
|
||||
### AI-Generated Spec (created in `openspec/specs/auth/spec.md`):
|
||||
|
||||
```markdown
|
||||
# Auth Specification
|
||||
|
||||
## Purpose
|
||||
Authentication and session management.
|
||||
|
||||
## Requirements
|
||||
### Requirement: User Authentication
|
||||
The system SHALL issue a JWT on successful login.
|
||||
|
||||
#### Scenario: Valid credentials
|
||||
- WHEN a user submits valid credentials
|
||||
- THEN a JWT is returned
|
||||
```
|
||||
|
||||
### AI-Generated Change Delta (created in `openspec/changes/add-2fa/specs/auth/spec.md`):
|
||||
|
||||
```markdown
|
||||
# Delta for Auth
|
||||
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
The system MUST require a second factor during login.
|
||||
|
||||
#### Scenario: OTP required
|
||||
- WHEN a user submits valid credentials
|
||||
- THEN an OTP challenge is required
|
||||
```
|
||||
|
||||
### AI-Generated Tasks (created in `openspec/changes/add-2fa/tasks.md`):
|
||||
|
||||
```markdown
|
||||
## 1. Database Setup
|
||||
- [ ] 1.1 Add OTP secret column to users table
|
||||
- [ ] 1.2 Create OTP verification logs table
|
||||
|
||||
## 2. Backend Implementation
|
||||
- [ ] 2.1 Add OTP generation endpoint
|
||||
- [ ] 2.2 Modify login flow to require OTP
|
||||
- [ ] 2.3 Add OTP verification endpoint
|
||||
|
||||
## 3. Frontend Updates
|
||||
- [ ] 3.1 Create OTP input component
|
||||
- [ ] 3.2 Update login flow UI
|
||||
```
|
||||
|
||||
**Important:** You don't create these files manually. Your AI assistant generates them based on your requirements and the existing codebase.
|
||||
|
||||
## Understanding OpenSpec Files
|
||||
|
||||
### Delta Format
|
||||
|
||||
Deltas are "patches" that show how specs change:
|
||||
|
||||
- **`## ADDED Requirements`** - New capabilities
|
||||
- **`## MODIFIED Requirements`** - Changed behavior (include complete updated text)
|
||||
- **`## REMOVED Requirements`** - Deprecated features
|
||||
|
||||
**Format requirements:**
|
||||
- Use `### Requirement: <name>` for headers
|
||||
- Every requirement needs at least one `#### Scenario:` block
|
||||
- Use SHALL/MUST in requirement text
|
||||
|
||||
## How OpenSpec Compares
|
||||
|
||||
### vs. spec-kit
|
||||
OpenSpec’s two-folder model (`openspec/specs/` for the current truth, `openspec/changes/` for proposed updates) keeps state and diffs separate. This scales when you modify existing features or touch multiple specs. spec-kit is strong for greenfield/0→1 but provides less structure for cross-spec updates and evolving features.
|
||||
|
||||
### vs. Kiro.dev
|
||||
OpenSpec groups every change for a feature in one folder (`openspec/changes/feature-name/`), making it easy to track related specs, tasks, and designs together. Kiro spreads updates across multiple spec folders, which can make feature tracking harder.
|
||||
|
||||
### vs. No Specs
|
||||
Without specs, AI coding assistants generate code from vague prompts, often missing requirements or adding unwanted features. OpenSpec brings predictability by agreeing on the desired behavior before any code is written.
|
||||
|
||||
## Team Adoption
|
||||
|
||||
1. **Initialize OpenSpec** – Run `openspec init` in your repo.
|
||||
2. **Start with new features** – Ask your AI to capture upcoming work as change proposals.
|
||||
3. **Grow incrementally** – Each change archives into living specs that document your system.
|
||||
4. **Stay flexible** – Different teammates can use Claude Code, CodeBuddy, Cursor, or any AGENTS.md-compatible tool while sharing the same specs.
|
||||
|
||||
Run `openspec update` whenever someone switches tools so your agents pick up the latest instructions and slash-command bindings.
|
||||
|
||||
## Updating OpenSpec
|
||||
|
||||
1. **Upgrade the package**
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
2. **Refresh agent instructions**
|
||||
- Run `openspec update` inside each project to regenerate AI guidance and ensure the latest slash commands are active.
|
||||
|
||||
## Experimental Features
|
||||
|
||||
<details>
|
||||
<summary><strong>🧪 OPSX: Fluid, Iterative Workflow</strong> (Claude Code only)</summary>
|
||||
|
||||
**Why this exists:**
|
||||
- Standard workflow is locked down — you can't tweak instructions or customize
|
||||
- When AI output is bad, you can't improve the prompts yourself
|
||||
- Same workflow for everyone, no way to match how your team works
|
||||
|
||||
**What's different:**
|
||||
- **Hackable** — edit templates and schemas yourself, test immediately, no rebuild
|
||||
- **Granular** — each artifact has its own instructions, test and tweak individually
|
||||
- **Customizable** — define your own workflows, artifacts, and dependencies
|
||||
- **Fluid** — no phase gates, update any artifact anytime
|
||||
|
||||
```
|
||||
You can always go back:
|
||||
|
||||
proposal ──→ specs ──→ design ──→ tasks ──→ implement
|
||||
▲ ▲ ▲ │
|
||||
└───────────┴──────────┴────────────────────┘
|
||||
```
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/opsx:new` | Start a new change |
|
||||
| `/opsx:continue` | Create the next artifact (based on what's ready) |
|
||||
| `/opsx:ff` | Fast-forward (all planning artifacts at once) |
|
||||
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
|
||||
| `/opsx:archive` | Archive when done |
|
||||
|
||||
**Setup:** `openspec experimental`
|
||||
|
||||
[Full documentation →](docs/opsx.md)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Telemetry</strong> – OpenSpec collects anonymous usage stats (opt-out: <code>OPENSPEC_TELEMETRY=0</code>)</summary>
|
||||
|
||||
We collect only command names and version to understand usage patterns. No arguments, paths, content, or PII. Automatically disabled in CI.
|
||||
|
||||
**Opt-out:** `export OPENSPEC_TELEMETRY=0` or `export DO_NOT_TRACK=1`
|
||||
|
||||
</details>
|
||||
|
||||
## Contributing
|
||||
|
||||
- Install dependencies: `pnpm install`
|
||||
- Build: `pnpm run build`
|
||||
- Test: `pnpm test`
|
||||
- Develop CLI locally: `pnpm run dev` or `pnpm run dev:cli`
|
||||
- Conventional commits (one-line): `type(scope): subject`
|
||||
|
||||
<details>
|
||||
<summary><strong>Maintainers & Advisors</strong></summary>
|
||||
|
||||
See [MAINTAINERS.md](MAINTAINERS.md) for the list of core maintainers and advisors who help guide the project.
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -1,597 +0,0 @@
|
||||
# POC-OpenSpec-Core Analysis
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions & Terminology
|
||||
|
||||
### Philosophy: Not a Workflow System
|
||||
|
||||
This system is **not** a workflow engine. It's an **artifact tracker with dependency awareness**.
|
||||
|
||||
| What it's NOT | What it IS |
|
||||
|---------------|------------|
|
||||
| Linear step-by-step progression | Exploratory, iterative planning |
|
||||
| Bureaucratic checkpoints | Enablers that unlock possibilities |
|
||||
| "You must complete step 1 first" | "Here's what you could create now" |
|
||||
| Form-filling | Fluid document creation |
|
||||
|
||||
**Key insight:** Dependencies are *enablers*, not *gates*. You can't meaningfully write a design document if there's no proposal to design from - that's not bureaucracy, it's logic.
|
||||
|
||||
### Terminology
|
||||
|
||||
| Term | Definition | Example |
|
||||
|------|------------|---------|
|
||||
| **Change** | A unit of work being planned (feature, refactor, migration) | `openspec/changes/add-auth/` |
|
||||
| **Schema** | An artifact graph definition (what artifacts exist, their dependencies) | `spec-driven.yaml` |
|
||||
| **Artifact** | A node in the graph (a document to create) | `proposal`, `design`, `specs` |
|
||||
| **Template** | Instructions/guidance for creating an artifact | `templates/proposal.md` |
|
||||
|
||||
### Hierarchy
|
||||
|
||||
```
|
||||
Schema (defines) ──→ Artifacts (guided by) ──→ Templates
|
||||
```
|
||||
|
||||
- **Schema** = the artifact graph (what exists, dependencies)
|
||||
- **Artifact** = a document to produce
|
||||
- **Template** = instructions for creating that artifact
|
||||
|
||||
### Schema Variations
|
||||
|
||||
Schemas can vary across multiple dimensions:
|
||||
|
||||
| Dimension | Examples |
|
||||
|-----------|----------|
|
||||
| Philosophy | `spec-driven`, `tdd`, `prototype-first` |
|
||||
| Version | `v1`, `v2`, `v3` |
|
||||
| Language | `en`, `zh`, `es` |
|
||||
| Custom | `team-alpha`, `experimental` |
|
||||
|
||||
### Schema Resolution (XDG Standard)
|
||||
|
||||
Schemas follow the XDG Base Directory Specification with a 2-level resolution:
|
||||
|
||||
```
|
||||
1. ${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # Global user override
|
||||
2. <package>/schemas/<name>/schema.yaml # Built-in defaults
|
||||
```
|
||||
|
||||
**Platform-specific paths:**
|
||||
- Unix/macOS: `~/.local/share/openspec/schemas/`
|
||||
- Windows: `%LOCALAPPDATA%/openspec/schemas/`
|
||||
- All platforms: `$XDG_DATA_HOME/openspec/schemas/` (when set)
|
||||
|
||||
**Why XDG?**
|
||||
- Schemas are workflow definitions (data), not user preferences (config)
|
||||
- Built-ins baked into package, never auto-copied
|
||||
- Users customize by creating files in global data dir
|
||||
- Consistent with modern CLI tooling standards
|
||||
|
||||
### Template Inheritance (2 Levels Max)
|
||||
|
||||
Templates are co-located with schemas in a `templates/` subdirectory:
|
||||
|
||||
```
|
||||
1. ${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
|
||||
2. <package>/schemas/<schema>/templates/<artifact>.md # Built-in
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- User overrides take precedence over package built-ins
|
||||
- A CLI command shows resolved paths (no guessing)
|
||||
- No inheritance between schemas (copy if you need to diverge)
|
||||
- Templates are always co-located with their schema
|
||||
|
||||
**Why this matters:**
|
||||
- Avoids "where does this come from?" debugging
|
||||
- No implicit magic that works until it doesn't
|
||||
- Schema + templates form a cohesive unit
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This is an **artifact tracker with dependency awareness** that guides iterative development through a structured artifact pipeline. The core innovation is using the **filesystem as a database** - artifact completion is detected by file existence, making the system stateless and version-control friendly.
|
||||
|
||||
The system answers:
|
||||
- "What artifacts exist for this change?"
|
||||
- "What could I create next?" (not "what must I create")
|
||||
- "What's blocking X?" (informational, not prescriptive)
|
||||
|
||||
---
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. ArtifactGraph (Slice 1 - COMPLETE)
|
||||
|
||||
The dependency graph engine with XDG-compliant schema resolution.
|
||||
|
||||
| Responsibility | Approach |
|
||||
|----------------|----------|
|
||||
| Model artifacts as a DAG | Artifact with `requires: string[]` |
|
||||
| Track completion state | `Set<string>` for completed artifacts |
|
||||
| Calculate build order | Kahn's algorithm (topological sort) |
|
||||
| Find ready artifacts | Check if all dependencies are in `completed` set |
|
||||
| Resolve schemas | XDG global → package built-ins |
|
||||
|
||||
**Key Data Structures (Zod-validated):**
|
||||
|
||||
```typescript
|
||||
// Zod schemas define types + validation
|
||||
const ArtifactSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
generates: z.string().min(1), // e.g., "proposal.md" or "specs/*.md"
|
||||
description: z.string(),
|
||||
template: z.string(), // path to template file
|
||||
requires: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
const SchemaYamlSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
version: z.number().int().positive(),
|
||||
description: z.string().optional(),
|
||||
artifacts: z.array(ArtifactSchema).min(1),
|
||||
});
|
||||
|
||||
// Derived types
|
||||
type Artifact = z.infer<typeof ArtifactSchema>;
|
||||
type SchemaYaml = z.infer<typeof SchemaYamlSchema>;
|
||||
```
|
||||
|
||||
**Key Methods:**
|
||||
- `resolveSchema(name)` - Load schema with XDG fallback
|
||||
- `ArtifactGraph.fromSchema(schema)` - Build graph from schema
|
||||
- `detectState(graph, changeDir)` - Scan filesystem for completion
|
||||
- `getNextArtifacts(graph, completed)` - Find artifacts ready to create
|
||||
- `getBuildOrder(graph)` - Topological sort of all artifacts
|
||||
- `getBlocked(graph, completed)` - Artifacts with unmet dependencies
|
||||
|
||||
---
|
||||
|
||||
### 2. Change Utilities (Slice 2)
|
||||
|
||||
Simple utility functions for programmatic change creation. No class, no abstraction layer.
|
||||
|
||||
| Responsibility | Approach |
|
||||
|----------------|----------|
|
||||
| Create changes | Create dirs under `openspec/changes/<name>/` with README |
|
||||
| Name validation | Enforce kebab-case naming |
|
||||
|
||||
**Key Paths:**
|
||||
|
||||
```
|
||||
openspec/changes/<name>/ → Change instances with artifacts (project-level)
|
||||
```
|
||||
|
||||
**Key Functions** (`src/utils/change-utils.ts`):
|
||||
- `createChange(projectRoot, name, description?)` - Create new change directory + README
|
||||
- `validateChangeName(name)` - Validate kebab-case naming, returns `{ valid, error? }`
|
||||
|
||||
**Note:** Existing CLI commands (`ListCommand`, `ChangeCommand`) already handle listing, path resolution, and existence checks. No need to extract that logic - it works fine as-is.
|
||||
|
||||
---
|
||||
|
||||
### 3. InstructionLoader (Slice 3)
|
||||
|
||||
Template resolution and instruction enrichment.
|
||||
|
||||
| Responsibility | Approach |
|
||||
|----------------|----------|
|
||||
| Resolve templates | XDG 2-level fallback (schema-specific → shared → built-in) |
|
||||
| Build dynamic context | Gather dependency status, change info |
|
||||
| Enrich templates | Inject context into base templates |
|
||||
| Generate status reports | Formatted markdown with progress |
|
||||
|
||||
**Key Class - ChangeState:**
|
||||
|
||||
```
|
||||
ChangeState {
|
||||
changeName: string
|
||||
changeDir: string
|
||||
graph: ArtifactGraph
|
||||
completed: Set<string>
|
||||
|
||||
// Methods
|
||||
getNextSteps(): string[]
|
||||
getStatus(artifactId): ArtifactStatus
|
||||
isComplete(): boolean
|
||||
}
|
||||
```
|
||||
|
||||
**Key Functions:**
|
||||
- `getTemplatePath(artifactId, schemaName?)` - Resolve with 2-level fallback
|
||||
- `getEnrichedInstructions(artifactId, projectRoot, changeName?)` - Main entry point
|
||||
- `getChangeStatus(projectRoot, changeName?)` - Formatted status report
|
||||
|
||||
---
|
||||
|
||||
### 4. CLI (Slice 4)
|
||||
|
||||
User interface layer. **All commands are deterministic** - require explicit `--change` parameter.
|
||||
|
||||
| Command | Function | Status |
|
||||
|---------|----------|--------|
|
||||
| `status --change <id>` | Show change progress (artifact graph) | **NEW** |
|
||||
| `next --change <id>` | Show artifacts ready to create | **NEW** |
|
||||
| `instructions <artifact> --change <id>` | Get enriched instructions for artifact | **NEW** |
|
||||
| `list` | List all changes | EXISTS (`openspec change list`) |
|
||||
| `new <name>` | Create change | **NEW** (uses `createChange()`) |
|
||||
| `init` | Initialize structure | EXISTS (`openspec init`) |
|
||||
| `templates --change <id>` | Show resolved template paths | **NEW** |
|
||||
|
||||
**Note:** Commands that operate on a change require `--change`. Missing parameter → error with list of available changes. Agent infers the change from conversation and passes it explicitly.
|
||||
|
||||
**Existing CLI commands** (not part of this slice):
|
||||
- `openspec change list` / `openspec change show <id>` / `openspec change validate <id>`
|
||||
- `openspec list --changes` / `openspec list --specs`
|
||||
- `openspec view` (dashboard)
|
||||
- `openspec init` / `openspec archive <change>`
|
||||
|
||||
---
|
||||
|
||||
### 5. Claude Commands
|
||||
|
||||
Integration layer for Claude Code. **Operational commands only** - artifact creation via natural language.
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/status` | Show change progress |
|
||||
| `/next` | Show what's ready to create |
|
||||
| `/run [artifact]` | Execute a specific step (power users) |
|
||||
| `/list` | List all changes |
|
||||
| `/new <name>` | Create a new change |
|
||||
| `/init` | Initialize structure |
|
||||
|
||||
**Artifact creation:** Users say "create the proposal" or "write the tests" in natural language. The agent:
|
||||
1. Infers change from conversation (confirms if uncertain)
|
||||
2. Infers artifact from request
|
||||
3. Calls CLI with explicit `--change` parameter
|
||||
4. Creates artifact following instructions
|
||||
|
||||
This works for ANY artifact in ANY schema - no new slash commands needed when schemas change.
|
||||
|
||||
**Note:** Legacy commands (`/openspec-proposal`, `/openspec-apply`, `/openspec-archive`) exist in the main project for backward compatibility but are separate from this architecture.
|
||||
|
||||
---
|
||||
|
||||
## Component Dependency Graph
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PRESENTATION LAYER │
|
||||
│ ┌──────────────┐ ┌────────────────────┐ │
|
||||
│ │ CLI │ ←─shell exec───────│ Claude Commands │ │
|
||||
│ └──────┬───────┘ └────────────────────┘ │
|
||||
└─────────┼───────────────────────────────────────────────────┘
|
||||
│ imports
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ORCHESTRATION LAYER │
|
||||
│ ┌────────────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ InstructionLoader │ │ change-utils (Slice 2) │ │
|
||||
│ │ (Slice 3) │ │ createChange() │ │
|
||||
│ └─────────┬──────────┘ │ validateChangeName() │ │
|
||||
│ │ └──────────────────────────┘ │
|
||||
└────────────┼────────────────────────────────────────────────┘
|
||||
│ uses
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ CORE LAYER │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ ArtifactGraph (Slice 1) │ │
|
||||
│ │ │ │
|
||||
│ │ Schema Resolution (XDG) ──→ Graph ──→ State Detection│ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ reads from
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PERSISTENCE LAYER │
|
||||
│ ┌──────────────────┐ ┌────────────────────────────────┐ │
|
||||
│ │ XDG Schemas │ │ Project Artifacts │ │
|
||||
│ │ ~/.local/share/ │ │ openspec/changes/<name>/ │ │
|
||||
│ │ openspec/ │ │ - proposal.md, design.md │ │
|
||||
│ │ schemas/ │ │ - specs/*.md, tasks.md │ │
|
||||
│ └──────────────────┘ └────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Design Patterns
|
||||
|
||||
### 1. Filesystem as Database
|
||||
|
||||
No SQLite, no JSON state files. The existence of `proposal.md` means proposal is complete.
|
||||
|
||||
```
|
||||
// State detection is just file existence checking
|
||||
if (exists(artifactPath)) {
|
||||
completed.add(artifactId)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Deterministic CLI, Inferring Agent
|
||||
|
||||
**CLI layer:** Always deterministic - requires explicit `--change` parameter.
|
||||
|
||||
```
|
||||
openspec status --change add-auth # explicit, works
|
||||
openspec status # error: "No change specified"
|
||||
```
|
||||
|
||||
**Agent layer:** Infers from conversation, confirms if uncertain, passes explicit `--change`.
|
||||
|
||||
This separation means:
|
||||
- CLI is pure, testable, no state to corrupt
|
||||
- Agent handles all "smartness"
|
||||
- No config.yaml tracking of "active change"
|
||||
|
||||
### 3. XDG-Compliant Schema Resolution
|
||||
|
||||
```
|
||||
${XDG_DATA_HOME}/openspec/schemas/<name>/schema.yaml # User override
|
||||
↓ (not found)
|
||||
<package>/schemas/<name>/schema.yaml # Built-in
|
||||
↓ (not found)
|
||||
Error (schema not found)
|
||||
```
|
||||
|
||||
### 4. Two-Level Template Fallback
|
||||
|
||||
```
|
||||
${XDG_DATA_HOME}/openspec/schemas/<schema>/templates/<artifact>.md # User override
|
||||
↓ (not found)
|
||||
<package>/schemas/<schema>/templates/<artifact>.md # Built-in
|
||||
↓ (not found)
|
||||
Error (no silent fallback to avoid confusion)
|
||||
```
|
||||
|
||||
### 5. Glob Pattern Support
|
||||
|
||||
`specs/*.md` allows multiple files to satisfy a single artifact:
|
||||
|
||||
```
|
||||
if (artifact.generates.includes("*")) {
|
||||
const parentDir = changeDir / patternParts[0]
|
||||
if (exists(parentDir) && hasFiles(parentDir)) {
|
||||
completed.add(artifactId)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Stateless State Detection
|
||||
|
||||
Every command re-scans the filesystem. No cached state to corrupt.
|
||||
|
||||
---
|
||||
|
||||
## Artifact Pipeline (Default Schema)
|
||||
|
||||
The default `spec-driven` schema:
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ proposal │ (no dependencies)
|
||||
└────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ specs │ (requires: proposal)
|
||||
└────┬─────┘
|
||||
│
|
||||
├──────────────┐
|
||||
▼ ▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ design │ │ │
|
||||
│ │◄──┤ proposal │
|
||||
└────┬─────┘ └──────────┘
|
||||
│ (requires: proposal, specs)
|
||||
▼
|
||||
┌──────────┐
|
||||
│ tasks │ (requires: design)
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
Other schemas (TDD, prototype-first) would have different graphs.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
Structured as **vertical slices** - each slice is independently testable.
|
||||
|
||||
---
|
||||
|
||||
### Slice 1: "What's Ready?" (Core Query) ✅ COMPLETE
|
||||
|
||||
**Delivers:** Types + Graph + State Detection + Schema Resolution
|
||||
|
||||
**Implementation:** `src/core/artifact-graph/`
|
||||
- `types.ts` - Zod schemas and derived TypeScript types
|
||||
- `schema.ts` - YAML parsing with Zod validation
|
||||
- `graph.ts` - ArtifactGraph class with topological sort
|
||||
- `state.ts` - Filesystem-based state detection
|
||||
- `resolver.ts` - XDG-compliant schema resolution
|
||||
- `builtin-schemas.ts` - Package-bundled default schemas
|
||||
|
||||
**Key decisions made:**
|
||||
- Zod for schema validation (consistent with project)
|
||||
- XDG for global schema overrides
|
||||
- `Set<string>` for completion state (immutable, functional)
|
||||
- `inProgress` and `failed` states deferred (require external tracking)
|
||||
|
||||
---
|
||||
|
||||
### Slice 2: "Change Creation Utilities"
|
||||
|
||||
**Delivers:** Utility functions for programmatic change creation
|
||||
|
||||
**Scope:**
|
||||
- `createChange(projectRoot, name, description?)` → creates directory + README
|
||||
- `validateChangeName(name)` → kebab-case pattern enforcement
|
||||
|
||||
**Not in scope (already exists in CLI commands):**
|
||||
- `listChanges()` → exists in `ListCommand` and `ChangeCommand.getActiveChanges()`
|
||||
- `getChangePath()` → simple `path.join()` inline
|
||||
- `changeExists()` → simple `fs.access()` inline
|
||||
- `isInitialized()` → simple directory check inline
|
||||
|
||||
**Why simplified:** Extracting existing CLI logic into a class would require similar refactoring of `SpecCommand` for consistency. The existing code works fine (~15 lines each). Only truly new functionality is `createChange()` + name validation.
|
||||
|
||||
---
|
||||
|
||||
### Slice 3: "Get Instructions" (Enrichment)
|
||||
|
||||
**Delivers:** Template resolution + context injection
|
||||
|
||||
**Testable behaviors:**
|
||||
- Template fallback: schema-specific → shared → built-in → error
|
||||
- Context injection: completed deps show ✓, missing show ✗
|
||||
- Output path shown correctly based on change directory
|
||||
|
||||
---
|
||||
|
||||
### Slice 4: "CLI + Integration"
|
||||
|
||||
**Delivers:** New artifact graph commands (builds on existing CLI)
|
||||
|
||||
**New commands:**
|
||||
- `status --change <id>` - Show artifact completion state
|
||||
- `next --change <id>` - Show ready-to-create artifacts
|
||||
- `instructions <artifact> --change <id>` - Get enriched template
|
||||
- `templates --change <id>` - Show resolved paths
|
||||
- `new <name>` - Create change (wrapper for `createChange()`)
|
||||
|
||||
**Already exists (not in scope):**
|
||||
- `openspec change list/show/validate` - change management
|
||||
- `openspec list --changes/--specs` - listing
|
||||
- `openspec view` - dashboard
|
||||
- `openspec init` - initialization
|
||||
|
||||
**Testable behaviors:**
|
||||
- Each new command produces expected output
|
||||
- Commands compose correctly (status → next → instructions flow)
|
||||
- Error handling for missing changes, invalid artifacts, etc.
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
# Global (XDG paths - user overrides)
|
||||
~/.local/share/openspec/ # Unix/macOS ($XDG_DATA_HOME/openspec/)
|
||||
%LOCALAPPDATA%/openspec/ # Windows
|
||||
└── schemas/ # Schema overrides
|
||||
└── custom-workflow/ # User-defined schema directory
|
||||
├── schema.yaml # Schema definition
|
||||
└── templates/ # Co-located templates
|
||||
└── proposal.md
|
||||
|
||||
# Package (built-in defaults)
|
||||
<package>/
|
||||
└── schemas/ # Built-in schema definitions
|
||||
├── spec-driven/ # Default: proposal → specs → design → tasks
|
||||
│ ├── schema.yaml
|
||||
│ └── templates/
|
||||
│ ├── proposal.md
|
||||
│ ├── design.md
|
||||
│ ├── spec.md
|
||||
│ └── tasks.md
|
||||
└── tdd/ # TDD: tests → implementation → docs
|
||||
├── schema.yaml
|
||||
└── templates/
|
||||
├── test.md
|
||||
├── implementation.md
|
||||
├── spec.md
|
||||
└── docs.md
|
||||
|
||||
# Project (change instances)
|
||||
openspec/
|
||||
└── changes/ # Change instances
|
||||
├── add-auth/
|
||||
│ ├── README.md # Auto-generated on creation
|
||||
│ ├── proposal.md # Created artifacts
|
||||
│ ├── design.md
|
||||
│ └── specs/
|
||||
│ └── *.md
|
||||
├── refactor-db/
|
||||
│ └── ...
|
||||
└── archive/ # Completed changes
|
||||
└── 2025-01-01-add-auth/
|
||||
|
||||
.claude/
|
||||
├── settings.local.json # Permissions
|
||||
└── commands/ # Slash commands
|
||||
└── *.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Schema YAML Format
|
||||
|
||||
```yaml
|
||||
# Built-in: <package>/schemas/spec-driven/schema.yaml
|
||||
# Or user override: ~/.local/share/openspec/schemas/spec-driven/schema.yaml
|
||||
name: spec-driven
|
||||
version: 1
|
||||
description: Specification-driven development
|
||||
|
||||
artifacts:
|
||||
- id: proposal
|
||||
generates: "proposal.md"
|
||||
description: "Create project proposal document"
|
||||
template: "proposal.md" # resolves from co-located templates/ directory
|
||||
requires: []
|
||||
|
||||
- id: specs
|
||||
generates: "specs/*.md" # glob pattern
|
||||
description: "Create technical specification documents"
|
||||
template: "specs.md"
|
||||
requires:
|
||||
- proposal
|
||||
|
||||
- id: design
|
||||
generates: "design.md"
|
||||
description: "Create design document"
|
||||
template: "design.md"
|
||||
requires:
|
||||
- proposal
|
||||
- specs
|
||||
|
||||
- id: tasks
|
||||
generates: "tasks.md"
|
||||
description: "Create tasks breakdown document"
|
||||
template: "tasks.md"
|
||||
requires:
|
||||
- design
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Layer | Component | Responsibility | Status |
|
||||
|-------|-----------|----------------|--------|
|
||||
| Core | ArtifactGraph | Pure dependency logic + XDG schema resolution | ✅ Slice 1 COMPLETE |
|
||||
| Utils | change-utils | Change creation + name validation only | Slice 2 (new functionality only) |
|
||||
| Core | InstructionLoader | Template resolution + enrichment | Slice 3 (all new) |
|
||||
| Presentation | CLI | New artifact graph commands | Slice 4 (new commands only) |
|
||||
| Integration | Claude Commands | AI assistant glue | Slice 4 |
|
||||
|
||||
**What already exists (not in this proposal):**
|
||||
- `getActiveChangeIds()` in `src/utils/item-discovery.ts` - list changes
|
||||
- `ChangeCommand.list/show/validate()` in `src/commands/change.ts`
|
||||
- `ListCommand.execute()` in `src/core/list.ts`
|
||||
- `ViewCommand.execute()` in `src/core/view.ts` - dashboard
|
||||
- `src/core/init.ts` - initialization
|
||||
- `src/core/archive.ts` - archiving
|
||||
|
||||
**Key Principles:**
|
||||
- **Filesystem IS the database** - stateless, version-control friendly
|
||||
- **Dependencies are enablers** - show what's possible, don't force order
|
||||
- **Deterministic CLI, inferring agent** - CLI requires explicit `--change`, agent infers from context
|
||||
- **XDG-compliant paths** - schemas and templates use standard user data directories
|
||||
- **2-level inheritance** - user override → package built-in (no deeper)
|
||||
- **Schemas are versioned** - support variations by philosophy, version, language
|
||||
+894
@@ -0,0 +1,894 @@
|
||||
# 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).
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Commands | Purpose |
|
||||
|----------|----------|---------|
|
||||
| **Setup** | `init`, `update` | Initialize and update OpenSpec in your project |
|
||||
| **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 |
|
||||
| **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 |
|
||||
|
||||
---
|
||||
|
||||
## Human vs Agent Commands
|
||||
|
||||
Most CLI commands are designed for **human use** in a terminal. Some commands also support **agent/script use** via JSON output.
|
||||
|
||||
### Human-Only Commands
|
||||
|
||||
These commands are interactive and designed for terminal use:
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `openspec init` | Initialize project (interactive prompts) |
|
||||
| `openspec view` | Interactive dashboard |
|
||||
| `openspec config edit` | Open config in editor |
|
||||
| `openspec feedback` | Submit feedback via GitHub |
|
||||
| `openspec completion install` | Install shell completions |
|
||||
|
||||
### Agent-Compatible Commands
|
||||
|
||||
These commands support `--json` output for programmatic use by AI agents and scripts:
|
||||
|
||||
| Command | Human Use | Agent Use |
|
||||
|---------|-----------|-----------|
|
||||
| `openspec list` | Browse changes/specs | `--json` for structured data |
|
||||
| `openspec show <item>` | Read content | `--json` for parsing |
|
||||
| `openspec validate` | Check for issues | `--all --json` for bulk validation |
|
||||
| `openspec status` | See artifact progress | `--json` for structured status |
|
||||
| `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 |
|
||||
|
||||
---
|
||||
|
||||
## Global Options
|
||||
|
||||
These options work with all commands:
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--version`, `-V` | Show version number |
|
||||
| `--no-color` | Disable color output |
|
||||
| `--help`, `-h` | Display help for command |
|
||||
|
||||
---
|
||||
|
||||
## Setup Commands
|
||||
|
||||
### `openspec init`
|
||||
|
||||
Initialize OpenSpec in your project. Creates the folder structure and configures AI tool integrations.
|
||||
|
||||
```
|
||||
openspec init [path] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `path` | No | Target directory (default: current directory) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--tools <list>` | Configure AI tools non-interactively. Use `all`, `none`, or comma-separated list |
|
||||
| `--force` | Auto-cleanup legacy files without prompting |
|
||||
|
||||
**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`
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive initialization
|
||||
openspec init
|
||||
|
||||
# Initialize in a specific directory
|
||||
openspec init ./my-project
|
||||
|
||||
# Non-interactive: configure for Claude and Cursor
|
||||
openspec init --tools claude,cursor
|
||||
|
||||
# Configure for all supported tools
|
||||
openspec init --tools all
|
||||
|
||||
# Skip prompts and auto-cleanup legacy files
|
||||
openspec init --force
|
||||
```
|
||||
|
||||
**What it creates:**
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── specs/ # Your specifications (source of truth)
|
||||
├── changes/ # Proposed changes
|
||||
└── config.yaml # Project configuration
|
||||
|
||||
.claude/skills/ # Claude Code skill files (if claude selected)
|
||||
.cursor/rules/ # Cursor rules (if cursor selected)
|
||||
... (other tool configs)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec update`
|
||||
|
||||
Update OpenSpec instruction files after upgrading the CLI. Re-generates AI tool configuration files.
|
||||
|
||||
```
|
||||
openspec update [path] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `path` | No | Target directory (default: current directory) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--force` | Force update even when files are up to date |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Update instruction files after npm upgrade
|
||||
npm update @fission-ai/openspec
|
||||
openspec update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Browsing Commands
|
||||
|
||||
### `openspec list`
|
||||
|
||||
List changes or specs in your project.
|
||||
|
||||
```
|
||||
openspec list [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--specs` | List specs instead of changes |
|
||||
| `--changes` | List changes (default) |
|
||||
| `--sort <order>` | Sort by `recent` (default) or `name` |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# List all active changes
|
||||
openspec list
|
||||
|
||||
# List all specs
|
||||
openspec list --specs
|
||||
|
||||
# JSON output for scripts
|
||||
openspec list --json
|
||||
```
|
||||
|
||||
**Output (text):**
|
||||
|
||||
```
|
||||
Active changes:
|
||||
add-dark-mode UI theme switching support
|
||||
fix-login-bug Session timeout handling
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec view`
|
||||
|
||||
Display an interactive dashboard for exploring specs and changes.
|
||||
|
||||
```
|
||||
openspec view
|
||||
```
|
||||
|
||||
Opens a terminal-based interface for navigating your project's specifications and changes.
|
||||
|
||||
---
|
||||
|
||||
### `openspec show`
|
||||
|
||||
Display details of a change or spec.
|
||||
|
||||
```
|
||||
openspec show [item-name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `item-name` | No | Name of change or spec (prompts if omitted) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--type <type>` | Specify type: `change` or `spec` (auto-detected if unambiguous) |
|
||||
| `--json` | Output as JSON |
|
||||
| `--no-interactive` | Disable prompts |
|
||||
|
||||
**Change-specific options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--deltas-only` | Show only delta specs (JSON mode) |
|
||||
|
||||
**Spec-specific options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--requirements` | Show only requirements, exclude scenarios (JSON mode) |
|
||||
| `--no-scenarios` | Exclude scenario content (JSON mode) |
|
||||
| `-r, --requirement <id>` | Show specific requirement by 1-based index (JSON mode) |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive selection
|
||||
openspec show
|
||||
|
||||
# Show a specific change
|
||||
openspec show add-dark-mode
|
||||
|
||||
# Show a specific spec
|
||||
openspec show auth --type spec
|
||||
|
||||
# JSON output for parsing
|
||||
openspec show add-dark-mode --json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation Commands
|
||||
|
||||
### `openspec validate`
|
||||
|
||||
Validate changes and specs for structural issues.
|
||||
|
||||
```
|
||||
openspec validate [item-name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `item-name` | No | Specific item to validate (prompts if omitted) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--all` | Validate all changes and specs |
|
||||
| `--changes` | Validate all changes |
|
||||
| `--specs` | Validate all specs |
|
||||
| `--type <type>` | Specify type when name is ambiguous: `change` or `spec` |
|
||||
| `--strict` | Enable strict validation mode |
|
||||
| `--json` | Output as JSON |
|
||||
| `--concurrency <n>` | Max parallel validations (default: 6, or `OPENSPEC_CONCURRENCY` env) |
|
||||
| `--no-interactive` | Disable prompts |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive validation
|
||||
openspec validate
|
||||
|
||||
# Validate a specific change
|
||||
openspec validate add-dark-mode
|
||||
|
||||
# Validate all changes
|
||||
openspec validate --changes
|
||||
|
||||
# Validate everything with JSON output (for CI/scripts)
|
||||
openspec validate --all --json
|
||||
|
||||
# Strict validation with increased parallelism
|
||||
openspec validate --all --strict --concurrency 12
|
||||
```
|
||||
|
||||
**Output (text):**
|
||||
|
||||
```
|
||||
Validating add-dark-mode...
|
||||
✓ proposal.md valid
|
||||
✓ specs/ui/spec.md valid
|
||||
⚠ design.md: missing "Technical Approach" section
|
||||
|
||||
1 warning found
|
||||
```
|
||||
|
||||
**Output (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"results": {
|
||||
"changes": [
|
||||
{
|
||||
"name": "add-dark-mode",
|
||||
"valid": true,
|
||||
"warnings": ["design.md: missing 'Technical Approach' section"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"total": 1,
|
||||
"valid": 1,
|
||||
"invalid": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle Commands
|
||||
|
||||
### `openspec archive`
|
||||
|
||||
Archive a completed change and merge delta specs into main specs.
|
||||
|
||||
```
|
||||
openspec archive [change-name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Change to archive (prompts if omitted) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-y, --yes` | Skip confirmation prompts |
|
||||
| `--skip-specs` | Skip spec updates (for infrastructure/tooling/doc-only changes) |
|
||||
| `--no-validate` | Skip validation (requires confirmation) |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive archive
|
||||
openspec archive
|
||||
|
||||
# Archive specific change
|
||||
openspec archive add-dark-mode
|
||||
|
||||
# Archive without prompts (CI/scripts)
|
||||
openspec archive add-dark-mode --yes
|
||||
|
||||
# Archive a tooling change that doesn't affect specs
|
||||
openspec archive update-ci-config --skip-specs
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
|
||||
1. Validates the change (unless `--no-validate`)
|
||||
2. Prompts for confirmation (unless `--yes`)
|
||||
3. Merges delta specs into `openspec/specs/`
|
||||
4. Moves change folder to `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
||||
|
||||
---
|
||||
|
||||
## Workflow Commands
|
||||
|
||||
These commands support the artifact-driven OPSX workflow. They're useful for both humans checking progress and agents determining next steps.
|
||||
|
||||
### `openspec status`
|
||||
|
||||
Display artifact completion status for a change.
|
||||
|
||||
```
|
||||
openspec status [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--change <id>` | Change name (prompts if omitted) |
|
||||
| `--schema <name>` | Schema override (auto-detected from change's config) |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive status check
|
||||
openspec status
|
||||
|
||||
# Status for specific change
|
||||
openspec status --change add-dark-mode
|
||||
|
||||
# JSON for agent use
|
||||
openspec status --change add-dark-mode --json
|
||||
```
|
||||
|
||||
**Output (text):**
|
||||
|
||||
```
|
||||
Change: add-dark-mode
|
||||
Schema: spec-driven
|
||||
|
||||
Artifacts:
|
||||
✓ proposal proposal.md exists
|
||||
✓ specs specs/ exists
|
||||
◆ design ready (requires: specs)
|
||||
○ tasks blocked (requires: design)
|
||||
|
||||
Next: Create design using /opsx:continue
|
||||
```
|
||||
|
||||
**Output (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"change": "add-dark-mode",
|
||||
"schema": "spec-driven",
|
||||
"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"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec instructions`
|
||||
|
||||
Get enriched instructions for creating an artifact or applying tasks. Used by AI agents to understand what to create next.
|
||||
|
||||
```
|
||||
openspec instructions [artifact] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `artifact` | No | Artifact ID: `proposal`, `specs`, `design`, `tasks`, or `apply` |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--change <id>` | Change name (required in non-interactive mode) |
|
||||
| `--schema <name>` | Schema override |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Special case:** Use `apply` as the artifact to get task implementation instructions.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Get instructions for next artifact
|
||||
openspec instructions --change add-dark-mode
|
||||
|
||||
# Get specific artifact instructions
|
||||
openspec instructions design --change add-dark-mode
|
||||
|
||||
# Get apply/implementation instructions
|
||||
openspec instructions apply --change add-dark-mode
|
||||
|
||||
# JSON for agent consumption
|
||||
openspec instructions design --change add-dark-mode --json
|
||||
```
|
||||
|
||||
**Output includes:**
|
||||
|
||||
- Template content for the artifact
|
||||
- Project context from config
|
||||
- Content from dependency artifacts
|
||||
- Per-artifact rules from config
|
||||
|
||||
---
|
||||
|
||||
### `openspec templates`
|
||||
|
||||
Show resolved template paths for all artifacts in a schema.
|
||||
|
||||
```
|
||||
openspec templates [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--schema <name>` | Schema to inspect (default: `spec-driven`) |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Show template paths for default schema
|
||||
openspec templates
|
||||
|
||||
# Show templates for custom schema
|
||||
openspec templates --schema my-workflow
|
||||
|
||||
# JSON for programmatic use
|
||||
openspec templates --json
|
||||
```
|
||||
|
||||
**Output (text):**
|
||||
|
||||
```
|
||||
Schema: spec-driven
|
||||
|
||||
Templates:
|
||||
proposal → ~/.openspec/schemas/spec-driven/templates/proposal.md
|
||||
specs → ~/.openspec/schemas/spec-driven/templates/specs.md
|
||||
design → ~/.openspec/schemas/spec-driven/templates/design.md
|
||||
tasks → ~/.openspec/schemas/spec-driven/templates/tasks.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec schemas`
|
||||
|
||||
List available workflow schemas with their descriptions and artifact flows.
|
||||
|
||||
```
|
||||
openspec schemas [options]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
openspec schemas
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
Available schemas:
|
||||
|
||||
spec-driven (package)
|
||||
The default spec-driven development workflow
|
||||
Flow: proposal → specs → design → tasks
|
||||
|
||||
my-custom (project)
|
||||
Custom workflow for this project
|
||||
Flow: research → proposal → tasks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Schema Commands
|
||||
|
||||
Commands for creating and managing custom workflow schemas.
|
||||
|
||||
### `openspec schema init`
|
||||
|
||||
Create a new project-local schema.
|
||||
|
||||
```
|
||||
openspec schema init <name> [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `name` | Yes | Schema name (kebab-case) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--description <text>` | Schema description |
|
||||
| `--artifacts <list>` | Comma-separated artifact IDs (default: `proposal,specs,design,tasks`) |
|
||||
| `--default` | Set as project default schema |
|
||||
| `--no-default` | Don't prompt to set as default |
|
||||
| `--force` | Overwrite existing schema |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Interactive schema creation
|
||||
openspec schema init research-first
|
||||
|
||||
# Non-interactive with specific artifacts
|
||||
openspec schema init rapid \
|
||||
--description "Rapid iteration workflow" \
|
||||
--artifacts "proposal,tasks" \
|
||||
--default
|
||||
```
|
||||
|
||||
**What it creates:**
|
||||
|
||||
```
|
||||
openspec/schemas/<name>/
|
||||
├── schema.yaml # Schema definition
|
||||
└── templates/
|
||||
├── proposal.md # Template for each artifact
|
||||
├── specs.md
|
||||
├── design.md
|
||||
└── tasks.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec schema fork`
|
||||
|
||||
Copy an existing schema to your project for customization.
|
||||
|
||||
```
|
||||
openspec schema fork <source> [name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `source` | Yes | Schema to copy |
|
||||
| `name` | No | New schema name (default: `<source>-custom`) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--force` | Overwrite existing destination |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Fork the built-in spec-driven schema
|
||||
openspec schema fork spec-driven my-workflow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec schema validate`
|
||||
|
||||
Validate a schema's structure and templates.
|
||||
|
||||
```
|
||||
openspec schema validate [name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `name` | No | Schema to validate (validates all if omitted) |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--verbose` | Show detailed validation steps |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Validate a specific schema
|
||||
openspec schema validate my-workflow
|
||||
|
||||
# Validate all schemas
|
||||
openspec schema validate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec schema which`
|
||||
|
||||
Show where a schema resolves from (useful for debugging precedence).
|
||||
|
||||
```
|
||||
openspec schema which [name] [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `name` | No | Schema name |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--all` | List all schemas with their sources |
|
||||
| `--json` | Output as JSON |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Check where a schema comes from
|
||||
openspec schema which spec-driven
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
spec-driven resolves from: package
|
||||
Source: /usr/local/lib/node_modules/@fission-ai/openspec/schemas/spec-driven
|
||||
```
|
||||
|
||||
**Schema precedence:**
|
||||
|
||||
1. Project: `openspec/schemas/<name>/`
|
||||
2. User: `~/.local/share/openspec/schemas/<name>/`
|
||||
3. Package: Built-in schemas
|
||||
|
||||
---
|
||||
|
||||
## Configuration Commands
|
||||
|
||||
### `openspec config`
|
||||
|
||||
View and modify global OpenSpec configuration.
|
||||
|
||||
```
|
||||
openspec config <subcommand> [options]
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `path` | Show config file location |
|
||||
| `list` | Show all current settings |
|
||||
| `get <key>` | Get a specific value |
|
||||
| `set <key> <value>` | Set a value |
|
||||
| `unset <key>` | Remove a key |
|
||||
| `reset` | Reset to defaults |
|
||||
| `edit` | Open in `$EDITOR` |
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Show config file path
|
||||
openspec config path
|
||||
|
||||
# List all settings
|
||||
openspec config list
|
||||
|
||||
# Get a specific value
|
||||
openspec config get telemetry.enabled
|
||||
|
||||
# Set a value
|
||||
openspec config set telemetry.enabled false
|
||||
|
||||
# Set a string value explicitly
|
||||
openspec config set user.name "My Name" --string
|
||||
|
||||
# Remove a custom setting
|
||||
openspec config unset user.name
|
||||
|
||||
# Reset all configuration
|
||||
openspec config reset --all --yes
|
||||
|
||||
# Edit config in your editor
|
||||
openspec config edit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utility Commands
|
||||
|
||||
### `openspec feedback`
|
||||
|
||||
Submit feedback about OpenSpec. Creates a GitHub issue.
|
||||
|
||||
```
|
||||
openspec feedback <message> [options]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `message` | Yes | Feedback message |
|
||||
|
||||
**Options:**
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `--body <text>` | Detailed description |
|
||||
|
||||
**Requirements:** GitHub CLI (`gh`) must be installed and authenticated.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
openspec feedback "Add support for custom artifact types" \
|
||||
--body "I'd like to define my own artifact types beyond the built-in ones."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `openspec completion`
|
||||
|
||||
Manage shell completions for the OpenSpec CLI.
|
||||
|
||||
```
|
||||
openspec completion <subcommand> [shell]
|
||||
```
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
| Subcommand | Description |
|
||||
|------------|-------------|
|
||||
| `generate [shell]` | Output completion script to stdout |
|
||||
| `install [shell]` | Install completion for your shell |
|
||||
| `uninstall [shell]` | Remove installed completions |
|
||||
|
||||
**Supported shells:** `bash`, `zsh`, `fish`, `powershell`
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Install completions (auto-detects shell)
|
||||
openspec completion install
|
||||
|
||||
# Install for specific shell
|
||||
openspec completion install zsh
|
||||
|
||||
# Generate script for manual installation
|
||||
openspec completion generate bash > ~/.bash_completion.d/openspec
|
||||
|
||||
# Uninstall
|
||||
openspec completion uninstall
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `0` | Success |
|
||||
| `1` | Error (validation failure, missing files, etc.) |
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OPENSPEC_CONCURRENCY` | Default concurrency for bulk validation (default: 6) |
|
||||
| `EDITOR` or `VISUAL` | Editor for `openspec config edit` |
|
||||
| `NO_COLOR` | Disable color output when set |
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Commands](commands.md) - AI slash commands (`/opsx:new`, `/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
|
||||
@@ -0,0 +1,652 @@
|
||||
# Commands
|
||||
|
||||
This is the reference for OpenSpec's slash commands. These commands are invoked in your AI coding assistant's chat interface (e.g., Claude Code, Cursor, Windsurf).
|
||||
|
||||
For workflow patterns and when to use each command, see [Workflows](workflows.md). For CLI commands, see [CLI](cli.md).
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/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:sync` | Merge delta specs into main specs |
|
||||
| `/opsx:archive` | Archive a completed change |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes at once |
|
||||
| `/opsx:onboard` | Guided tutorial through the complete workflow |
|
||||
|
||||
---
|
||||
|
||||
## Command Reference
|
||||
|
||||
### `/opsx:explore`
|
||||
|
||||
Think through ideas, investigate problems, and clarify requirements before committing to a change.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:explore [topic]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `topic` | No | What you want to explore or investigate |
|
||||
|
||||
**What it does:**
|
||||
- Opens an exploratory conversation with no structure required
|
||||
- Investigates the codebase to answer questions
|
||||
- Compares options and approaches
|
||||
- Creates visual diagrams to clarify thinking
|
||||
- Can transition to `/opsx:new` when insights crystallize
|
||||
|
||||
**Example:**
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: How should we handle authentication for the mobile app?
|
||||
|
||||
AI: Let me investigate your current auth setup...
|
||||
[Analyzes codebase]
|
||||
|
||||
I see you're using session-based auth for the web app.
|
||||
For mobile, we have three main options:
|
||||
|
||||
1. JWT tokens (stateless, works offline)
|
||||
2. OAuth2 (if you need third-party login)
|
||||
3. Extend current sessions (simpler, but requires connectivity)
|
||||
|
||||
Your API already has CORS configured. Which direction interests you?
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Use when requirements are unclear or you need to investigate
|
||||
- No artifacts are created during exploration
|
||||
- Good for comparing multiple approaches before deciding
|
||||
- Can read files and search the codebase
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:new`
|
||||
|
||||
Start a new change. Creates the change folder structure and scaffolds it with the selected schema.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:new [change-name] [--schema <schema-name>]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Name for the change folder (prompted if not provided) |
|
||||
| `--schema` | No | Workflow schema to use (default: from config or `spec-driven`) |
|
||||
|
||||
**What it does:**
|
||||
- Creates `openspec/changes/<change-name>/` directory
|
||||
- Creates `.openspec.yaml` metadata file in the change folder
|
||||
- Shows the first artifact template ready for creation
|
||||
- Prompts for change name and schema if not provided
|
||||
|
||||
**What it creates:**
|
||||
```
|
||||
openspec/changes/<change-name>/
|
||||
└── .openspec.yaml # Change metadata (schema, created date)
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:new add-dark-mode
|
||||
|
||||
AI: Created openspec/changes/add-dark-mode/
|
||||
Schema: spec-driven
|
||||
|
||||
Ready to create: proposal
|
||||
Use /opsx:continue to create it, or /opsx:ff to create all artifacts.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Use descriptive names: `add-feature`, `fix-bug`, `refactor-module`
|
||||
- Avoid generic names like `update`, `changes`, `wip`
|
||||
- Schema can also be set in project config (`openspec/config.yaml`)
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:continue`
|
||||
|
||||
Create the next artifact in the dependency chain. Creates one artifact at a time for incremental progress.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:continue [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to continue (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Queries the artifact dependency graph
|
||||
- Shows which artifacts are ready vs blocked
|
||||
- Creates the first ready artifact
|
||||
- Reads dependency files for context
|
||||
- Shows what becomes available after creation
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:continue
|
||||
|
||||
AI: Change: add-dark-mode
|
||||
|
||||
Artifact status:
|
||||
✓ proposal (done)
|
||||
◆ specs (ready)
|
||||
◆ design (ready)
|
||||
○ tasks (blocked - needs: specs)
|
||||
|
||||
Creating specs...
|
||||
|
||||
[Reads proposal.md, creates specs based on requirements]
|
||||
|
||||
✓ Created openspec/changes/add-dark-mode/specs/ui/spec.md
|
||||
|
||||
Now available: tasks
|
||||
Run /opsx:continue to create the next artifact.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Use when you want to review each artifact before proceeding
|
||||
- Good for complex changes where you want control
|
||||
- Multiple artifacts may become ready simultaneously
|
||||
- You can edit created artifacts before continuing
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:ff`
|
||||
|
||||
Fast-forward through artifact creation. Creates all planning artifacts at once.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:ff [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to fast-forward (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Creates all artifacts in dependency order
|
||||
- Tracks progress via todo list
|
||||
- Stops when all `apply-required` artifacts are complete
|
||||
- Reads each dependency before creating the next artifact
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:ff add-dark-mode
|
||||
|
||||
AI: Fast-forwarding add-dark-mode...
|
||||
|
||||
✓ Creating proposal.md
|
||||
✓ Creating specs/ui/spec.md
|
||||
✓ Creating design.md
|
||||
✓ Creating tasks.md
|
||||
|
||||
All planning artifacts complete!
|
||||
Ready for implementation. Run /opsx:apply to begin.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Use when you have a clear picture of what you're building
|
||||
- Faster than `/opsx:continue` for straightforward changes
|
||||
- You can still edit artifacts afterward
|
||||
- Good for small to medium features
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:apply`
|
||||
|
||||
Implement tasks from the change. Works through the task list, writing code and checking off items.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:apply [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to implement (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Reads `tasks.md` and identifies incomplete tasks
|
||||
- Works through tasks one by one
|
||||
- Writes code, creates files, runs tests as needed
|
||||
- Marks tasks complete with checkboxes `[x]`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Implementing add-dark-mode...
|
||||
|
||||
Reading tasks.md:
|
||||
- [ ] 1.1 Create ThemeContext
|
||||
- [ ] 1.2 Add CSS custom properties
|
||||
- [ ] 1.3 Implement localStorage persistence
|
||||
|
||||
Working on 1.1: Create ThemeContext...
|
||||
[Creates src/context/ThemeContext.tsx]
|
||||
|
||||
✓ 1.1 Complete
|
||||
|
||||
Working on 1.2: Add CSS custom properties...
|
||||
[Updates src/styles/globals.css]
|
||||
|
||||
✓ 1.2 Complete
|
||||
...
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Can resume where you left off if interrupted
|
||||
- Use for parallel changes by specifying the change name
|
||||
- Completion state is tracked in `tasks.md` checkboxes
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:verify`
|
||||
|
||||
Validate that implementation matches your change artifacts. Checks completeness, correctness, and coherence.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:verify [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to verify (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Checks three dimensions of implementation quality
|
||||
- Searches codebase for implementation evidence
|
||||
- Reports issues categorized as CRITICAL, WARNING, or SUGGESTION
|
||||
- Does not block archive, but surfaces issues
|
||||
|
||||
**Verification dimensions:**
|
||||
|
||||
| Dimension | What it validates |
|
||||
|-----------|-------------------|
|
||||
| **Completeness** | All tasks done, all requirements implemented, scenarios covered |
|
||||
| **Correctness** | Implementation matches spec intent, edge cases handled |
|
||||
| **Coherence** | Design decisions reflected in code, patterns consistent |
|
||||
|
||||
**Example:**
|
||||
```
|
||||
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 "System preference detection" has no test coverage
|
||||
|
||||
CORRECTNESS
|
||||
✓ Implementation matches spec intent
|
||||
✓ Edge cases from scenarios are handled
|
||||
✓ Error states match spec definitions
|
||||
|
||||
COHERENCE
|
||||
✓ Design decisions reflected in code structure
|
||||
✓ Naming conventions consistent with design.md
|
||||
⚠ Design mentions "CSS variables" but implementation uses Tailwind classes
|
||||
|
||||
SUMMARY
|
||||
─────────────────────────────
|
||||
Critical issues: 0
|
||||
Warnings: 2
|
||||
Ready to archive: Yes (with warnings)
|
||||
|
||||
Recommendations:
|
||||
1. Add test for system preference detection
|
||||
2. Update design.md to reflect Tailwind usage, or refactor to use CSS variables
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Run before archiving to catch mismatches early
|
||||
- Warnings don't block archive but indicate potential issues
|
||||
- Good for reviewing AI's work before committing
|
||||
- Can reveal drift between artifacts and implementation
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:sync`
|
||||
|
||||
**Optional command.** Merge delta specs from a change into main specs. Archive will prompt to sync if needed, so you typically don't need to run this manually.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:sync [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to sync (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Reads delta specs from change folder
|
||||
- Parses ADDED/MODIFIED/REMOVED/RENAMED sections
|
||||
- Merges changes into main `openspec/specs/` directory
|
||||
- Preserves existing content not mentioned in delta
|
||||
- Does not archive the change (remains active)
|
||||
|
||||
**Example:**
|
||||
```text
|
||||
You: /opsx:sync
|
||||
|
||||
AI: Syncing add-dark-mode delta specs...
|
||||
|
||||
Reading: openspec/changes/add-dark-mode/specs/ui/spec.md
|
||||
Target: openspec/specs/ui/spec.md
|
||||
|
||||
Changes to apply:
|
||||
✓ ADDED: Theme Selection requirement (2 scenarios)
|
||||
✓ ADDED: Accessibility requirement (1 scenario)
|
||||
|
||||
Merging...
|
||||
✓ openspec/specs/ui/spec.md updated
|
||||
|
||||
Change remains active. Run /opsx:archive when ready to complete.
|
||||
```
|
||||
|
||||
**When to use manually:**
|
||||
|
||||
| Scenario | Use sync? |
|
||||
|----------|-----------|
|
||||
| Long-running change, want specs in main before archiving | Yes |
|
||||
| Multiple parallel changes need the updated base specs | Yes |
|
||||
| Want to preview/review the merge separately | Yes |
|
||||
| Quick change, going straight to archive | No (archive handles it) |
|
||||
|
||||
**Tips:**
|
||||
- Sync is intelligent, not copy-paste
|
||||
- Can add scenarios to existing requirements without duplicating
|
||||
- Change stays active after sync (not archived)
|
||||
- Most users will never need to call this directly—archive prompts if needed
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:archive`
|
||||
|
||||
Archive a completed change. Finalizes the change and moves it to the archive folder.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:archive [change-name]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-name` | No | Which change to archive (inferred from context if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Checks artifact completion status
|
||||
- Checks task completion (warns if incomplete)
|
||||
- Offers to sync delta specs if not already synced
|
||||
- Moves change folder to `openspec/changes/archive/YYYY-MM-DD-<name>/`
|
||||
- Preserves all artifacts for audit trail
|
||||
|
||||
**Example:**
|
||||
```text
|
||||
You: /opsx:archive
|
||||
|
||||
AI: Archiving add-dark-mode...
|
||||
|
||||
Artifact status:
|
||||
✓ proposal.md exists
|
||||
✓ specs/ exists
|
||||
✓ design.md exists
|
||||
✓ tasks.md exists (8/8 tasks complete)
|
||||
|
||||
Delta specs: Not yet synced
|
||||
→ Sync now? (recommended)
|
||||
|
||||
You: Yes
|
||||
|
||||
AI: ✓ Synced specs to openspec/specs/ui/spec.md
|
||||
✓ Moved to openspec/changes/archive/2025-01-24-add-dark-mode/
|
||||
|
||||
Change archived successfully.
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Archive won't block on incomplete tasks, but will warn
|
||||
- Delta specs can be synced during archive or beforehand
|
||||
- Archived changes are preserved for history
|
||||
- Use `/opsx:verify` first to catch issues
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:bulk-archive`
|
||||
|
||||
Archive multiple completed changes at once. Handles spec conflicts between changes.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:bulk-archive [change-names...]
|
||||
```
|
||||
|
||||
**Arguments:**
|
||||
| Argument | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `change-names` | No | Specific changes to archive (prompts to select if not provided) |
|
||||
|
||||
**What it does:**
|
||||
- Lists all completed changes
|
||||
- Validates each change before archiving
|
||||
- Detects spec conflicts across changes
|
||||
- Resolves conflicts by checking what's actually implemented
|
||||
- Archives in chronological order
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:bulk-archive
|
||||
|
||||
AI: Found 3 completed changes:
|
||||
- add-dark-mode (8/8 tasks complete)
|
||||
- fix-login-bug (3/3 tasks complete)
|
||||
- update-footer (2/2 tasks complete)
|
||||
|
||||
Checking for spec conflicts...
|
||||
⚠ add-dark-mode and update-footer both touch specs/ui/
|
||||
|
||||
Inspecting codebase to resolve...
|
||||
Both changes are implemented. Will merge in chronological order:
|
||||
1. add-dark-mode (created Jan 20)
|
||||
2. update-footer (created Jan 22)
|
||||
|
||||
Archive all 3 changes?
|
||||
|
||||
You: Yes
|
||||
|
||||
AI: ✓ Archived add-dark-mode
|
||||
✓ Archived fix-login-bug
|
||||
✓ Archived update-footer
|
||||
Specs merged: add-dark-mode → update-footer
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Good for parallel work streams
|
||||
- Conflict resolution is agentic (checks codebase)
|
||||
- Changes are archived in order of creation
|
||||
- Prompts before overwriting spec content
|
||||
|
||||
---
|
||||
|
||||
### `/opsx:onboard`
|
||||
|
||||
Guided onboarding through the complete OpenSpec workflow. An interactive tutorial using your actual codebase.
|
||||
|
||||
**Syntax:**
|
||||
```
|
||||
/opsx:onboard
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Walks through a complete workflow cycle with narration
|
||||
- Scans your codebase for real improvement opportunities
|
||||
- Creates an actual change with real artifacts
|
||||
- Implements actual work (small, safe changes)
|
||||
- Archives the completed change
|
||||
- Explains each step as it happens
|
||||
|
||||
**Phases:**
|
||||
1. Welcome and codebase analysis
|
||||
2. Finding an improvement opportunity
|
||||
3. Creating a change (`/opsx:new`)
|
||||
4. Writing the proposal
|
||||
5. Creating specs
|
||||
6. Writing the design
|
||||
7. Creating tasks
|
||||
8. Implementing tasks (`/opsx:apply`)
|
||||
9. Verifying implementation
|
||||
10. Archiving the change
|
||||
11. Summary and next steps
|
||||
|
||||
**Example:**
|
||||
```
|
||||
You: /opsx:onboard
|
||||
|
||||
AI: Welcome to OpenSpec!
|
||||
|
||||
I'll walk you through the complete workflow using your actual codebase.
|
||||
We'll find something small to improve, create a proper change for it,
|
||||
implement it, and archive it.
|
||||
|
||||
Let me scan your codebase for opportunities...
|
||||
|
||||
[Analyzes codebase]
|
||||
|
||||
I found a few things we could work on:
|
||||
1. Add input validation to the contact form
|
||||
2. Improve error messages in the auth flow
|
||||
3. Add loading states to async buttons
|
||||
|
||||
Which interests you? (or suggest something else)
|
||||
```
|
||||
|
||||
**Tips:**
|
||||
- Best for new users learning the workflow
|
||||
- Uses real code, not toy examples
|
||||
- Creates a real change you can keep or discard
|
||||
- Takes 15-30 minutes to complete
|
||||
|
||||
---
|
||||
|
||||
## Command Syntax by AI Tool
|
||||
|
||||
Different AI tools use slightly different command syntax. Use the format that matches your tool:
|
||||
|
||||
| Tool | Syntax Example |
|
||||
|------|----------------|
|
||||
| Claude Code | `/opsx:new`, `/opsx:apply` |
|
||||
| Cursor | `/opsx-new`, `/opsx-apply` |
|
||||
| Windsurf | `/opsx-new`, `/opsx-apply` |
|
||||
| Copilot | `/opsx-new`, `/opsx-apply` |
|
||||
|
||||
The functionality is identical regardless of syntax.
|
||||
|
||||
---
|
||||
|
||||
## Legacy Commands
|
||||
|
||||
These commands use the older "all-at-once" workflow. They still work but OPSX commands are recommended.
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/openspec:proposal` | Create all artifacts at once (proposal, specs, design, tasks) |
|
||||
| `/openspec:apply` | Implement the change |
|
||||
| `/openspec:archive` | Archive the change |
|
||||
|
||||
**When to use legacy commands:**
|
||||
- Existing projects using the old workflow
|
||||
- Simple changes where you don't need incremental artifact creation
|
||||
- Preference for the all-or-nothing approach
|
||||
|
||||
**Migrating to OPSX:**
|
||||
Legacy changes can be continued with OPSX commands. The artifact structure is compatible.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Change not found"
|
||||
|
||||
The command couldn't identify which change to work on.
|
||||
|
||||
**Solutions:**
|
||||
- Specify the change name explicitly: `/opsx:apply add-dark-mode`
|
||||
- Check that the change folder exists: `openspec list`
|
||||
- Verify you're in the right project directory
|
||||
|
||||
### "No artifacts ready"
|
||||
|
||||
All artifacts are either complete or blocked by missing dependencies.
|
||||
|
||||
**Solutions:**
|
||||
- Run `openspec status --change <name>` to see what's blocking
|
||||
- Check if required artifacts exist
|
||||
- Create missing dependency artifacts first
|
||||
|
||||
### "Schema not found"
|
||||
|
||||
The specified schema doesn't exist.
|
||||
|
||||
**Solutions:**
|
||||
- List available schemas: `openspec schemas`
|
||||
- Check spelling of schema name
|
||||
- Create the schema if it's custom: `openspec schema init <name>`
|
||||
|
||||
### Commands not recognized
|
||||
|
||||
The AI tool doesn't recognize OpenSpec commands.
|
||||
|
||||
**Solutions:**
|
||||
- Ensure OpenSpec is initialized: `openspec init`
|
||||
- Regenerate skills: `openspec update`
|
||||
- Check that `.claude/skills/` directory exists (for Claude Code)
|
||||
- Restart your AI tool to pick up new skills
|
||||
|
||||
### Artifacts not generating properly
|
||||
|
||||
The AI creates incomplete or incorrect artifacts.
|
||||
|
||||
**Solutions:**
|
||||
- Add project context in `openspec/config.yaml`
|
||||
- Add per-artifact rules for specific guidance
|
||||
- Provide more detail in your change description
|
||||
- Use `/opsx:continue` instead of `/opsx:ff` for more control
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Workflows](workflows.md) - Common patterns and when to use each command
|
||||
- [CLI](cli.md) - Terminal commands for management and validation
|
||||
- [Customization](customization.md) - Create custom schemas and workflows
|
||||
@@ -0,0 +1,582 @@
|
||||
# Concepts
|
||||
|
||||
This guide explains the core ideas behind OpenSpec and how they fit together. For practical usage, see [Getting Started](getting-started.md) and [Workflows](workflows.md).
|
||||
|
||||
## Philosophy
|
||||
|
||||
OpenSpec is built around four principles:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Why These Principles Matter
|
||||
|
||||
**Fluid not rigid.** Traditional spec systems lock you into phases: first you plan, then you implement, then you're done. OpenSpec is more flexible — you can create artifacts in any order that makes sense for your work.
|
||||
|
||||
**Iterative not waterfall.** Requirements change. Understanding deepens. What seemed like a good approach at the start might not hold up after you see the codebase. OpenSpec embraces this reality.
|
||||
|
||||
**Easy not complex.** Some spec frameworks require extensive setup, rigid formats, or heavyweight processes. OpenSpec stays out of your way. Initialize in seconds, start working immediately, customize only if you need to.
|
||||
|
||||
**Brownfield-first.** Most software work isn't building from scratch — it's modifying existing systems. OpenSpec's delta-based approach makes it easy to specify changes to existing behavior, not just describe new systems.
|
||||
|
||||
## The Big Picture
|
||||
|
||||
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 │ │
|
||||
│ │ │ │ │ │
|
||||
│ └─────────────────────┘ └──────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Specs** are the source of truth — they describe how your system currently behaves.
|
||||
|
||||
**Changes** are proposed modifications — they live in separate folders until you're ready to merge them.
|
||||
|
||||
This separation is key. You can work on multiple changes in parallel without conflicts. You can review a change before it affects the main specs. And when you archive a change, its deltas merge cleanly into the source of truth.
|
||||
|
||||
## Specs
|
||||
|
||||
Specs describe your system's behavior using structured requirements and scenarios.
|
||||
|
||||
### Structure
|
||||
|
||||
```
|
||||
openspec/specs/
|
||||
├── auth/
|
||||
│ └── spec.md # Authentication behavior
|
||||
├── payments/
|
||||
│ └── spec.md # Payment processing
|
||||
├── notifications/
|
||||
│ └── spec.md # Notification system
|
||||
└── ui/
|
||||
└── spec.md # UI behavior and themes
|
||||
```
|
||||
|
||||
Organize specs by domain — logical groupings that make sense for your system. Common patterns:
|
||||
|
||||
- **By feature area**: `auth/`, `payments/`, `search/`
|
||||
- **By component**: `api/`, `frontend/`, `workers/`
|
||||
- **By bounded context**: `ordering/`, `fulfillment/`, `inventory/`
|
||||
|
||||
### Spec Format
|
||||
|
||||
A spec contains requirements, and each requirement has scenarios:
|
||||
|
||||
```markdown
|
||||
# Auth Specification
|
||||
|
||||
## Purpose
|
||||
Authentication and session management for the application.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: User Authentication
|
||||
The system SHALL issue a JWT token upon successful login.
|
||||
|
||||
#### Scenario: Valid credentials
|
||||
- GIVEN a user with valid credentials
|
||||
- WHEN the user submits login form
|
||||
- THEN a JWT token is returned
|
||||
- AND the user is redirected to dashboard
|
||||
|
||||
#### Scenario: Invalid credentials
|
||||
- GIVEN invalid credentials
|
||||
- WHEN the user submits login form
|
||||
- THEN an error message is displayed
|
||||
- AND no token is issued
|
||||
|
||||
### Requirement: Session Expiration
|
||||
The system MUST expire sessions after 30 minutes of inactivity.
|
||||
|
||||
#### Scenario: Idle timeout
|
||||
- GIVEN an authenticated session
|
||||
- WHEN 30 minutes pass without activity
|
||||
- THEN the session is invalidated
|
||||
- AND the user must re-authenticate
|
||||
```
|
||||
|
||||
**Key elements:**
|
||||
|
||||
| Element | Purpose |
|
||||
|---------|---------|
|
||||
| `## Purpose` | High-level description of this spec's domain |
|
||||
| `### Requirement:` | A specific behavior the system must have |
|
||||
| `#### Scenario:` | A concrete example of the requirement in action |
|
||||
| SHALL/MUST/SHOULD | RFC 2119 keywords indicating requirement strength |
|
||||
|
||||
### Why Structure Specs This Way
|
||||
|
||||
**Requirements are the "what"** — they state what the system should do without specifying implementation.
|
||||
|
||||
**Scenarios are the "when"** — they provide concrete examples that can be verified. Good scenarios:
|
||||
- Are testable (you could write an automated test for them)
|
||||
- Cover both happy path and edge cases
|
||||
- Use Given/When/Then or similar structured format
|
||||
|
||||
**RFC 2119 keywords** (SHALL, MUST, SHOULD, MAY) communicate intent:
|
||||
- **MUST/SHALL** — absolute requirement
|
||||
- **SHOULD** — recommended, but exceptions exist
|
||||
- **MAY** — optional
|
||||
|
||||
## Changes
|
||||
|
||||
A change is a proposed modification to your system, packaged as a folder with everything needed to understand and implement it.
|
||||
|
||||
### Change Structure
|
||||
|
||||
```
|
||||
openspec/changes/add-dark-mode/
|
||||
├── proposal.md # Why and what
|
||||
├── design.md # How (technical approach)
|
||||
├── tasks.md # Implementation checklist
|
||||
├── .openspec.yaml # Change metadata (optional)
|
||||
└── specs/ # Delta specs
|
||||
└── ui/
|
||||
└── spec.md # What's changing in ui/spec.md
|
||||
```
|
||||
|
||||
Each change is self-contained. It has:
|
||||
- **Artifacts** — documents that capture intent, design, and tasks
|
||||
- **Delta specs** — specifications for what's being added, modified, or removed
|
||||
- **Metadata** — optional configuration for this specific change
|
||||
|
||||
### Why Changes Are Folders
|
||||
|
||||
Packaging a change as a folder has several benefits:
|
||||
|
||||
1. **Everything together.** Proposal, design, tasks, and specs live in one place. No hunting through different locations.
|
||||
|
||||
2. **Parallel work.** Multiple changes can exist simultaneously without conflicting. Work on `add-dark-mode` while `fix-auth-bug` is also in progress.
|
||||
|
||||
3. **Clean history.** When archived, changes move to `changes/archive/` with their full context preserved. You can look back and understand not just what changed, but why.
|
||||
|
||||
4. **Review-friendly.** A change folder is easy to review — open it, read the proposal, check the design, see the spec deltas.
|
||||
|
||||
## Artifacts
|
||||
|
||||
Artifacts are the documents within a change that guide the work.
|
||||
|
||||
### The Artifact Flow
|
||||
|
||||
```
|
||||
proposal ──────► specs ──────► design ──────► tasks ──────► implement
|
||||
│ │ │ │
|
||||
why what how steps
|
||||
+ scope changes approach to take
|
||||
```
|
||||
|
||||
Artifacts build on each other. Each artifact provides context for the next.
|
||||
|
||||
### Artifact Types
|
||||
|
||||
#### Proposal (`proposal.md`)
|
||||
|
||||
The proposal captures **intent**, **scope**, and **approach** at a high level.
|
||||
|
||||
```markdown
|
||||
# Proposal: Add Dark Mode
|
||||
|
||||
## Intent
|
||||
Users have requested a dark mode option to reduce eye strain
|
||||
during nighttime usage and match system preferences.
|
||||
|
||||
## Scope
|
||||
In scope:
|
||||
- Theme toggle in settings
|
||||
- System preference detection
|
||||
- Persist preference in localStorage
|
||||
|
||||
Out of scope:
|
||||
- Custom color themes (future work)
|
||||
- Per-page theme overrides
|
||||
|
||||
## Approach
|
||||
Use CSS custom properties for theming with a React context
|
||||
for state management. Detect system preference on first load,
|
||||
allow manual override.
|
||||
```
|
||||
|
||||
**When to update the proposal:**
|
||||
- Scope changes (narrowing or expanding)
|
||||
- Intent clarifies (better understanding of the problem)
|
||||
- Approach fundamentally shifts
|
||||
|
||||
#### Specs (delta specs in `specs/`)
|
||||
|
||||
Delta specs describe **what's changing** relative to the current specs. See [Delta Specs](#delta-specs) below.
|
||||
|
||||
#### Design (`design.md`)
|
||||
|
||||
The design captures **technical approach** and **architecture decisions**.
|
||||
|
||||
```markdown
|
||||
# Design: Add Dark Mode
|
||||
|
||||
## Technical Approach
|
||||
Theme state managed via React Context to avoid prop drilling.
|
||||
CSS custom properties enable runtime switching without class toggling.
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### Decision: Context over Redux
|
||||
Using React Context for theme state because:
|
||||
- Simple binary state (light/dark)
|
||||
- No complex state transitions
|
||||
- Avoids adding Redux dependency
|
||||
|
||||
### Decision: CSS Custom Properties
|
||||
Using CSS variables instead of CSS-in-JS because:
|
||||
- Works with existing stylesheet
|
||||
- No runtime overhead
|
||||
- Browser-native solution
|
||||
|
||||
## Data Flow
|
||||
```
|
||||
ThemeProvider (context)
|
||||
│
|
||||
▼
|
||||
ThemeToggle ◄──► localStorage
|
||||
│
|
||||
▼
|
||||
CSS Variables (applied to :root)
|
||||
```
|
||||
|
||||
## File Changes
|
||||
- `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
|
||||
- Better solution discovered
|
||||
- Dependencies or constraints change
|
||||
|
||||
#### Tasks (`tasks.md`)
|
||||
|
||||
Tasks are the **implementation checklist** — concrete steps with checkboxes.
|
||||
|
||||
```markdown
|
||||
# Tasks
|
||||
|
||||
## 1. Theme Infrastructure
|
||||
- [ ] 1.1 Create ThemeContext with light/dark state
|
||||
- [ ] 1.2 Add CSS custom properties for colors
|
||||
- [ ] 1.3 Implement localStorage persistence
|
||||
- [ ] 1.4 Add system preference detection
|
||||
|
||||
## 2. UI Components
|
||||
- [ ] 2.1 Create ThemeToggle component
|
||||
- [ ] 2.2 Add toggle to settings page
|
||||
- [ ] 2.3 Update Header to include quick toggle
|
||||
|
||||
## 3. Styling
|
||||
- [ ] 3.1 Define dark theme color palette
|
||||
- [ ] 3.2 Update components to use CSS variables
|
||||
- [ ] 3.3 Test contrast ratios for accessibility
|
||||
```
|
||||
|
||||
**Task best practices:**
|
||||
- Group related tasks under headings
|
||||
- Use hierarchical numbering (1.1, 1.2, etc.)
|
||||
- Keep tasks small enough to complete in one session
|
||||
- Check tasks off as you complete them
|
||||
|
||||
## Delta Specs
|
||||
|
||||
Delta specs are the key concept that makes OpenSpec work for brownfield development. They describe **what's changing** rather than restating the entire spec.
|
||||
|
||||
### The Format
|
||||
|
||||
```markdown
|
||||
# Delta for Auth
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Two-Factor Authentication
|
||||
The system MUST support TOTP-based two-factor authentication.
|
||||
|
||||
#### Scenario: 2FA enrollment
|
||||
- GIVEN a user without 2FA enabled
|
||||
- WHEN the user enables 2FA in settings
|
||||
- THEN a QR code is displayed for authenticator app setup
|
||||
- AND the user must verify with a code before activation
|
||||
|
||||
#### Scenario: 2FA login
|
||||
- GIVEN a user with 2FA enabled
|
||||
- WHEN the user submits valid credentials
|
||||
- THEN an OTP challenge is presented
|
||||
- AND login completes only after valid OTP
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Session Expiration
|
||||
The system MUST expire sessions after 15 minutes of inactivity.
|
||||
(Previously: 30 minutes)
|
||||
|
||||
#### Scenario: Idle timeout
|
||||
- GIVEN an authenticated session
|
||||
- WHEN 15 minutes pass without activity
|
||||
- THEN the session is invalidated
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Remember Me
|
||||
(Deprecated in favor of 2FA. Users should re-authenticate each session.)
|
||||
```
|
||||
|
||||
### Delta Sections
|
||||
|
||||
| Section | Meaning | What Happens on Archive |
|
||||
|---------|---------|------------------------|
|
||||
| `## ADDED Requirements` | New behavior | Appended to main spec |
|
||||
| `## MODIFIED Requirements` | Changed behavior | Replaces existing requirement |
|
||||
| `## REMOVED Requirements` | Deprecated behavior | Deleted from main spec |
|
||||
|
||||
### Why Deltas Instead of Full Specs
|
||||
|
||||
**Clarity.** A delta shows exactly what's changing. Reading a full spec, you'd have to diff it mentally against the current version.
|
||||
|
||||
**Conflict avoidance.** Two changes can touch the same spec file without conflicting, as long as they modify different requirements.
|
||||
|
||||
**Review efficiency.** Reviewers see the change, not the unchanged context. Focus on what matters.
|
||||
|
||||
**Brownfield fit.** Most work modifies existing behavior. Deltas make modifications first-class, not an afterthought.
|
||||
|
||||
## Schemas
|
||||
|
||||
Schemas define the artifact types and their dependencies for a workflow.
|
||||
|
||||
### How Schemas Work
|
||||
|
||||
```yaml
|
||||
# openspec/schemas/spec-driven/schema.yaml
|
||||
name: spec-driven
|
||||
artifacts:
|
||||
- id: proposal
|
||||
generates: proposal.md
|
||||
requires: [] # No dependencies, can create first
|
||||
|
||||
- id: specs
|
||||
generates: specs/**/*.md
|
||||
requires: [proposal] # Needs proposal before creating
|
||||
|
||||
- id: design
|
||||
generates: design.md
|
||||
requires: [proposal] # Can create in parallel with specs
|
||||
|
||||
- id: tasks
|
||||
generates: tasks.md
|
||||
requires: [specs, design] # Needs both specs and design first
|
||||
```
|
||||
|
||||
**Artifacts form a dependency graph:**
|
||||
|
||||
```
|
||||
proposal
|
||||
(root node)
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
specs design
|
||||
(requires: (requires:
|
||||
proposal) proposal)
|
||||
│ │
|
||||
└─────────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
tasks
|
||||
(requires:
|
||||
specs, design)
|
||||
```
|
||||
|
||||
**Dependencies are enablers, not gates.** They show what's possible to create, not what you must create next. You can skip design if you don't need it. You can create specs before or after design — both depend only on proposal.
|
||||
|
||||
### Built-in Schemas
|
||||
|
||||
**spec-driven** (default)
|
||||
|
||||
The standard workflow for spec-driven development:
|
||||
|
||||
```
|
||||
proposal → specs → design → tasks → implement
|
||||
```
|
||||
|
||||
Best for: Most feature work where you want to agree on specs before implementation.
|
||||
|
||||
### Custom Schemas
|
||||
|
||||
Create custom schemas for your team's workflow:
|
||||
|
||||
```bash
|
||||
# Create from scratch
|
||||
openspec schema init research-first
|
||||
|
||||
# Or fork an existing one
|
||||
openspec schema fork spec-driven research-first
|
||||
```
|
||||
|
||||
**Example custom schema:**
|
||||
|
||||
```yaml
|
||||
# openspec/schemas/research-first/schema.yaml
|
||||
name: research-first
|
||||
artifacts:
|
||||
- id: research
|
||||
generates: research.md
|
||||
requires: [] # Do research first
|
||||
|
||||
- id: proposal
|
||||
generates: proposal.md
|
||||
requires: [research] # Proposal informed by research
|
||||
|
||||
- id: tasks
|
||||
generates: tasks.md
|
||||
requires: [proposal] # Skip specs/design, go straight to tasks
|
||||
```
|
||||
|
||||
See [Customization](customization.md) for full details on creating and using custom schemas.
|
||||
|
||||
## Archive
|
||||
|
||||
Archiving completes a change by merging its delta specs into the main specs and preserving the change for history.
|
||||
|
||||
### What Happens When You Archive
|
||||
|
||||
```
|
||||
Before archive:
|
||||
|
||||
openspec/
|
||||
├── specs/
|
||||
│ └── auth/
|
||||
│ └── spec.md ◄────────────────┐
|
||||
└── changes/ │
|
||||
└── add-2fa/ │
|
||||
├── proposal.md │
|
||||
├── design.md │ merge
|
||||
├── tasks.md │
|
||||
└── specs/ │
|
||||
└── auth/ │
|
||||
└── spec.md ─────────┘
|
||||
|
||||
|
||||
After archive:
|
||||
|
||||
openspec/
|
||||
├── specs/
|
||||
│ └── auth/
|
||||
│ └── spec.md # Now includes 2FA requirements
|
||||
└── changes/
|
||||
└── archive/
|
||||
└── 2025-01-24-add-2fa/ # Preserved for history
|
||||
├── proposal.md
|
||||
├── design.md
|
||||
├── tasks.md
|
||||
└── specs/
|
||||
└── auth/
|
||||
└── spec.md
|
||||
```
|
||||
|
||||
### The Archive Process
|
||||
|
||||
1. **Merge deltas.** Each delta spec section (ADDED/MODIFIED/REMOVED) is applied to the corresponding main spec.
|
||||
|
||||
2. **Move to archive.** The change folder moves to `changes/archive/` with a date prefix for chronological ordering.
|
||||
|
||||
3. **Preserve context.** All artifacts remain intact in the archive. You can always look back to understand why a change was made.
|
||||
|
||||
### Why Archive Matters
|
||||
|
||||
**Clean state.** Active changes (`changes/`) shows only work in progress. Completed work moves out of the way.
|
||||
|
||||
**Audit trail.** The archive preserves the full context of every change — not just what changed, but the proposal explaining why, the design explaining how, and the tasks showing the work done.
|
||||
|
||||
**Spec evolution.** Specs grow organically as changes are archived. Each archive merges its deltas, building up a comprehensive specification over time.
|
||||
|
||||
## How It All Fits Together
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ OPENSPEC FLOW │
|
||||
│ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 1. START │ /opsx:new creates a change folder │
|
||||
│ │ CHANGE │ │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 2. CREATE │ /opsx:ff or /opsx:continue │
|
||||
│ │ ARTIFACTS │ Creates proposal → specs → design → tasks │
|
||||
│ │ │ (based on schema dependencies) │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 3. IMPLEMENT │ /opsx:apply │
|
||||
│ │ TASKS │ Work through tasks, checking them off │
|
||||
│ │ │◄──── Update artifacts as you learn │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ 4. VERIFY │ /opsx:verify (optional) │
|
||||
│ │ WORK │ Check implementation matches specs │
|
||||
│ └───────┬────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ ┌──────────────────────────────────────────────┐ │
|
||||
│ │ 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:**
|
||||
|
||||
1. Specs describe current behavior
|
||||
2. Changes propose modifications (as deltas)
|
||||
3. Implementation makes the changes real
|
||||
4. Archive merges deltas into specs
|
||||
5. Specs now describe the new behavior
|
||||
6. Next change builds on updated specs
|
||||
|
||||
## Glossary
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| **Artifact** | A document within a change (proposal, design, tasks, or delta specs) |
|
||||
| **Archive** | The process of completing a change and merging its deltas into main specs |
|
||||
| **Change** | A proposed modification to the system, packaged as a folder with artifacts |
|
||||
| **Delta spec** | A spec that describes changes (ADDED/MODIFIED/REMOVED) relative to current specs |
|
||||
| **Domain** | A logical grouping for specs (e.g., `auth/`, `payments/`) |
|
||||
| **Requirement** | A specific behavior the system must have |
|
||||
| **Scenario** | A concrete example of a requirement, typically in Given/When/Then format |
|
||||
| **Schema** | A definition of artifact types and their dependencies |
|
||||
| **Spec** | A specification describing system behavior, containing requirements and scenarios |
|
||||
| **Source of truth** | The `openspec/specs/` directory, containing the current agreed-upon behavior |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Getting Started](getting-started.md) - Practical first steps
|
||||
- [Workflows](workflows.md) - Common patterns and when to use each
|
||||
- [Commands](commands.md) - Full command reference
|
||||
- [Customization](customization.md) - Create custom schemas and configure your project
|
||||
@@ -0,0 +1,342 @@
|
||||
# Customization
|
||||
|
||||
OpenSpec provides three levels of customization:
|
||||
|
||||
| Level | What it does | Best for |
|
||||
|-------|--------------|----------|
|
||||
| **Project Config** | Set defaults, inject context/rules | Most teams |
|
||||
| **Custom Schemas** | Define your own workflow artifacts | Teams with unique processes |
|
||||
| **Global Overrides** | Share schemas across all projects | Power users |
|
||||
|
||||
---
|
||||
|
||||
## Project Configuration
|
||||
|
||||
The `openspec/config.yaml` file is the easiest way to customize OpenSpec for your team. It lets you:
|
||||
|
||||
- **Set a default schema** - Skip `--schema` on every command
|
||||
- **Inject project context** - AI sees your tech stack, conventions, etc.
|
||||
- **Add per-artifact rules** - Custom rules for specific artifacts
|
||||
|
||||
### Quick Setup
|
||||
|
||||
```bash
|
||||
openspec init
|
||||
```
|
||||
|
||||
This walks you through creating a config interactively. Or create one manually:
|
||||
|
||||
```yaml
|
||||
# openspec/config.yaml
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Tech stack: TypeScript, React, Node.js, PostgreSQL
|
||||
API style: RESTful, documented in docs/api.md
|
||||
Testing: Jest + React Testing Library
|
||||
We value backwards compatibility for all public APIs
|
||||
|
||||
rules:
|
||||
proposal:
|
||||
- Include rollback plan
|
||||
- Identify affected teams
|
||||
specs:
|
||||
- Use Given/When/Then format
|
||||
- Reference existing patterns before inventing new ones
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
**Default schema:**
|
||||
|
||||
```bash
|
||||
# Without config
|
||||
openspec new change my-feature --schema spec-driven
|
||||
|
||||
# With config - schema is automatic
|
||||
openspec new change my-feature
|
||||
```
|
||||
|
||||
**Context and rules injection:**
|
||||
|
||||
When generating any artifact, your context and rules are injected into the AI prompt:
|
||||
|
||||
```xml
|
||||
<context>
|
||||
Tech stack: TypeScript, React, Node.js, PostgreSQL
|
||||
...
|
||||
</context>
|
||||
|
||||
<rules>
|
||||
- Include rollback plan
|
||||
- Identify affected teams
|
||||
</rules>
|
||||
|
||||
<template>
|
||||
[Schema's built-in template]
|
||||
</template>
|
||||
```
|
||||
|
||||
- **Context** appears in ALL artifacts
|
||||
- **Rules** ONLY appear for the matching artifact
|
||||
|
||||
### Schema Resolution Order
|
||||
|
||||
When OpenSpec needs a schema, it checks in this order:
|
||||
|
||||
1. CLI flag: `--schema <name>`
|
||||
2. Change metadata (`.openspec.yaml` in the change folder)
|
||||
3. Project config (`openspec/config.yaml`)
|
||||
4. Default (`spec-driven`)
|
||||
|
||||
---
|
||||
|
||||
## Custom Schemas
|
||||
|
||||
When project config isn't enough, create your own schema with a completely custom workflow. Custom schemas live in your project's `openspec/schemas/` directory and are version-controlled with your code.
|
||||
|
||||
```text
|
||||
your-project/
|
||||
├── openspec/
|
||||
│ ├── config.yaml # Project config
|
||||
│ ├── schemas/ # Custom schemas live here
|
||||
│ │ └── my-workflow/
|
||||
│ │ ├── schema.yaml
|
||||
│ │ └── templates/
|
||||
│ └── changes/ # Your changes
|
||||
└── src/
|
||||
```
|
||||
|
||||
### Fork an Existing Schema
|
||||
|
||||
The fastest way to customize is to fork a built-in schema:
|
||||
|
||||
```bash
|
||||
openspec schema fork spec-driven my-workflow
|
||||
```
|
||||
|
||||
This copies the entire `spec-driven` schema to `openspec/schemas/my-workflow/` where you can edit it freely.
|
||||
|
||||
**What you get:**
|
||||
|
||||
```text
|
||||
openspec/schemas/my-workflow/
|
||||
├── schema.yaml # Workflow definition
|
||||
└── templates/
|
||||
├── proposal.md # Template for proposal artifact
|
||||
├── spec.md # Template for specs
|
||||
├── design.md # Template for design
|
||||
└── tasks.md # Template for tasks
|
||||
```
|
||||
|
||||
Now edit `schema.yaml` to change the workflow, or edit templates to change what AI generates.
|
||||
|
||||
### Create a Schema from Scratch
|
||||
|
||||
For a completely fresh workflow:
|
||||
|
||||
```bash
|
||||
# Interactive
|
||||
openspec schema init research-first
|
||||
|
||||
# Non-interactive
|
||||
openspec schema init rapid \
|
||||
--description "Rapid iteration workflow" \
|
||||
--artifacts "proposal,tasks" \
|
||||
--default
|
||||
```
|
||||
|
||||
### Schema Structure
|
||||
|
||||
A schema defines the artifacts in your workflow and how they depend on each other:
|
||||
|
||||
```yaml
|
||||
# openspec/schemas/my-workflow/schema.yaml
|
||||
name: my-workflow
|
||||
version: 1
|
||||
description: My team's custom workflow
|
||||
|
||||
artifacts:
|
||||
- id: proposal
|
||||
generates: proposal.md
|
||||
description: Initial proposal document
|
||||
template: proposal.md
|
||||
instruction: |
|
||||
Create a proposal that explains WHY this change is needed.
|
||||
Focus on the problem, not the solution.
|
||||
requires: []
|
||||
|
||||
- id: design
|
||||
generates: design.md
|
||||
description: Technical design
|
||||
template: design.md
|
||||
instruction: |
|
||||
Create a design document explaining HOW to implement.
|
||||
requires:
|
||||
- proposal # Can't create design until proposal exists
|
||||
|
||||
- id: tasks
|
||||
generates: tasks.md
|
||||
description: Implementation checklist
|
||||
template: tasks.md
|
||||
requires:
|
||||
- design
|
||||
|
||||
apply:
|
||||
requires: [tasks]
|
||||
tracks: tasks.md
|
||||
```
|
||||
|
||||
**Key fields:**
|
||||
|
||||
| Field | Purpose |
|
||||
|-------|---------|
|
||||
| `id` | Unique identifier, used in commands and rules |
|
||||
| `generates` | Output filename (supports globs like `specs/**/*.md`) |
|
||||
| `template` | Template file in `templates/` directory |
|
||||
| `instruction` | AI instructions for creating this artifact |
|
||||
| `requires` | Dependencies - which artifacts must exist first |
|
||||
|
||||
### Templates
|
||||
|
||||
Templates are markdown files that guide the AI. They're injected into the prompt when creating that artifact.
|
||||
|
||||
```markdown
|
||||
<!-- templates/proposal.md -->
|
||||
## Why
|
||||
|
||||
<!-- Explain the motivation for this change. What problem does this solve? -->
|
||||
|
||||
## What Changes
|
||||
|
||||
<!-- Describe what will change. Be specific about new capabilities or modifications. -->
|
||||
|
||||
## Impact
|
||||
|
||||
<!-- Affected code, APIs, dependencies, systems -->
|
||||
```
|
||||
|
||||
Templates can include:
|
||||
- Section headers the AI should fill in
|
||||
- HTML comments with guidance for the AI
|
||||
- Example formats showing expected structure
|
||||
|
||||
### Validate Your Schema
|
||||
|
||||
Before using a custom schema, validate it:
|
||||
|
||||
```bash
|
||||
openspec schema validate my-workflow
|
||||
```
|
||||
|
||||
This checks:
|
||||
- `schema.yaml` syntax is correct
|
||||
- All referenced templates exist
|
||||
- No circular dependencies
|
||||
- Artifact IDs are valid
|
||||
|
||||
### Use Your Custom Schema
|
||||
|
||||
Once created, use your schema with:
|
||||
|
||||
```bash
|
||||
# Specify on command
|
||||
openspec new change feature --schema my-workflow
|
||||
|
||||
# Or set as default in config.yaml
|
||||
schema: my-workflow
|
||||
```
|
||||
|
||||
### Debug Schema Resolution
|
||||
|
||||
Not sure which schema is being used? Check with:
|
||||
|
||||
```bash
|
||||
# See where a specific schema resolves from
|
||||
openspec schema which my-workflow
|
||||
|
||||
# List all available schemas
|
||||
openspec schema which --all
|
||||
```
|
||||
|
||||
Output shows whether it's from your project, user directory, or the package:
|
||||
|
||||
```text
|
||||
Schema: my-workflow
|
||||
Source: project
|
||||
Path: /path/to/project/openspec/schemas/my-workflow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **Note:** OpenSpec also supports user-level schemas at `~/.local/share/openspec/schemas/` for sharing across projects, but project-level schemas in `openspec/schemas/` are recommended since they're version-controlled with your code.
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Rapid Iteration Workflow
|
||||
|
||||
A minimal workflow for quick iterations:
|
||||
|
||||
```yaml
|
||||
# openspec/schemas/rapid/schema.yaml
|
||||
name: rapid
|
||||
version: 1
|
||||
description: Fast iteration with minimal overhead
|
||||
|
||||
artifacts:
|
||||
- id: proposal
|
||||
generates: proposal.md
|
||||
description: Quick proposal
|
||||
template: proposal.md
|
||||
instruction: |
|
||||
Create a brief proposal for this change.
|
||||
Focus on what and why, skip detailed specs.
|
||||
requires: []
|
||||
|
||||
- id: tasks
|
||||
generates: tasks.md
|
||||
description: Implementation checklist
|
||||
template: tasks.md
|
||||
requires: [proposal]
|
||||
|
||||
apply:
|
||||
requires: [tasks]
|
||||
tracks: tasks.md
|
||||
```
|
||||
|
||||
### Adding a Review Artifact
|
||||
|
||||
Fork the default and add a review step:
|
||||
|
||||
```bash
|
||||
openspec schema fork spec-driven with-review
|
||||
```
|
||||
|
||||
Then edit `schema.yaml` to add:
|
||||
|
||||
```yaml
|
||||
- id: review
|
||||
generates: review.md
|
||||
description: Pre-implementation review checklist
|
||||
template: review.md
|
||||
instruction: |
|
||||
Create a review checklist based on the design.
|
||||
Include security, performance, and testing considerations.
|
||||
requires:
|
||||
- design
|
||||
|
||||
- id: tasks
|
||||
# ... existing tasks config ...
|
||||
requires:
|
||||
- specs
|
||||
- design
|
||||
- review # Now tasks require review too
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [CLI Reference: Schema Commands](cli.md#schema-commands) - Full command documentation
|
||||
@@ -1,926 +0,0 @@
|
||||
# OpenSpec Experimental Release Plan
|
||||
|
||||
This document outlines the plan to release the experimental artifact workflow system for user testing.
|
||||
|
||||
## Overview
|
||||
|
||||
The goal is to allow users to test the new artifact-driven workflow system alongside the existing OpenSpec commands. This experimental system (`opsx`) provides a more granular, step-by-step approach to creating change artifacts.
|
||||
|
||||
## Three Workflow Modes
|
||||
|
||||
### 1. Old Workflow (Current Production)
|
||||
- **Commands**: `/openspec:proposal`, `/openspec:apply`, `/openspec:archive`
|
||||
- **Behavior**: Hardcoded slash commands that generate all artifacts in one command
|
||||
- **Status**: Production, unchanged
|
||||
|
||||
### 2. New Artifact System - Batch Mode (Future)
|
||||
- **Commands**: Refactored `/openspec:proposal` using schemas
|
||||
- **Behavior**: Schema-driven but generates all artifacts at once (like legacy)
|
||||
- **Status**: Not in scope for this experimental release
|
||||
- **Note**: This is a future refactor to unify the old system with schemas
|
||||
|
||||
### 3. New Artifact System - Granular Mode (Experimental)
|
||||
- **Commands**: `/opsx:new`, `/opsx:continue`
|
||||
- **Behavior**: One artifact at a time, dependency-driven, iterative
|
||||
- **Status**: Target for this experimental release
|
||||
|
||||
---
|
||||
|
||||
## Work Items
|
||||
|
||||
### 1. Rename AWF to OPSX
|
||||
|
||||
**Current State:**
|
||||
- Commands: `/awf:start`, `/awf:continue`
|
||||
- Files: `.claude/commands/awf/start.md`, `.claude/commands/awf/continue.md`
|
||||
|
||||
**Target State:**
|
||||
- Commands: `/opsx:new`, `/opsx:continue`
|
||||
- Files: `.claude/commands/opsx/new.md`, `.claude/commands/opsx/continue.md`
|
||||
|
||||
**Tasks:**
|
||||
- [x] Create `.claude/commands/opsx/` directory
|
||||
- [x] Rename `start.md` → `new.md` and update content
|
||||
- [x] Copy `continue.md` with updated references
|
||||
- [x] Update all references from "awf" to "opsx" in command content
|
||||
- [x] Update frontmatter (name, description) to use "opsx" naming
|
||||
- [x] Remove `.claude/commands/awf/` directory
|
||||
|
||||
**CLI Commands:**
|
||||
The underlying CLI commands (`openspec status`, `openspec instructions`, etc.) remain unchanged. Only the slash command names change.
|
||||
|
||||
---
|
||||
|
||||
### 2. Remove WF Skill Files
|
||||
|
||||
**Current State:**
|
||||
- `.claude/commands/wf/start.md` - References non-existent `openspec wf` commands
|
||||
- `.claude/commands/wf/continue.md` - References non-existent `openspec wf` commands
|
||||
|
||||
**Target State:**
|
||||
- Directory and files removed
|
||||
|
||||
**Tasks:**
|
||||
- [x] Delete `.claude/commands/wf/start.md`
|
||||
- [x] Delete `.claude/commands/wf/continue.md`
|
||||
- [x] Delete `.claude/commands/wf/` directory
|
||||
|
||||
---
|
||||
|
||||
### 3. Add Agent Skills for Experimental Workflow
|
||||
|
||||
**Purpose:**
|
||||
Generate experimental workflow skills using the [Agent Skills](https://agentskills.io/specification) open standard.
|
||||
|
||||
**Why Skills Instead of Slash Commands:**
|
||||
- **Cross-editor compatibility**: Skills work in Claude Code, Cursor, Windsurf, and other compatible editors automatically
|
||||
- **Simpler implementation**: Single directory (`.claude/skills/`) instead of 18+ editor-specific configurators
|
||||
- **Standard format**: Open standard with simple YAML frontmatter + markdown
|
||||
- **User invocation**: Users explicitly invoke skills when they want to use them
|
||||
|
||||
**Behavior:**
|
||||
1. Create `.claude/skills/` directory if it doesn't exist
|
||||
2. Generate two skills using the Agent Skills specification:
|
||||
- `openspec-new-change/SKILL.md` - Start a new change with artifact workflow
|
||||
- `openspec-continue-change/SKILL.md` - Continue working on a change (create next artifact)
|
||||
3. Skills are added **alongside** existing `/openspec:*` commands (not replacing)
|
||||
|
||||
**Supported Editors:**
|
||||
- Claude Code (native support)
|
||||
- Cursor (native support via Settings → Rules → Import Settings)
|
||||
- Windsurf (imports `.claude` configs)
|
||||
- Cline, Codex, and other Agent Skills-compatible editors
|
||||
|
||||
**Tasks:**
|
||||
- [x] Create skill template content for `openspec-new-change` (based on current opsx:new)
|
||||
- [x] Create skill template content for `openspec-continue-change` (based on current opsx:continue)
|
||||
- [x] Add temporary `artifact-experimental-setup` command to CLI
|
||||
- [x] Implement skill file generation (YAML frontmatter + markdown body)
|
||||
- [x] Add success message with usage instructions
|
||||
|
||||
**Note:** The `artifact-experimental-setup` command is temporary and will be merged into `openspec init` once the experimental workflow is promoted to stable.
|
||||
|
||||
**Skill Format:**
|
||||
Each skill is a directory with a `SKILL.md` file:
|
||||
```
|
||||
.claude/skills/
|
||||
├── openspec-new-change/
|
||||
│ └── SKILL.md # name, description, instructions
|
||||
├── openspec-continue-change/
|
||||
│ └── SKILL.md # name, description, instructions
|
||||
└── openspec-apply-change/
|
||||
└── SKILL.md # name, description, instructions
|
||||
```
|
||||
|
||||
**CLI Interface:**
|
||||
```bash
|
||||
openspec artifact-experimental-setup
|
||||
|
||||
# Output:
|
||||
# 🧪 Experimental Artifact Workflow Skills Created
|
||||
#
|
||||
# ✓ .claude/skills/openspec-new-change/SKILL.md
|
||||
# ✓ .claude/skills/openspec-continue-change/SKILL.md
|
||||
# ✓ .claude/skills/openspec-apply-change/SKILL.md
|
||||
#
|
||||
# 📖 Usage:
|
||||
#
|
||||
# Skills work automatically in compatible editors:
|
||||
# • Claude Code - Auto-detected, ready to use
|
||||
# • Cursor - Enable in Settings → Rules → Import Settings
|
||||
# • Windsurf - Auto-imports from .claude directory
|
||||
#
|
||||
# Ask Claude naturally:
|
||||
# • "I want to start a new OpenSpec change to add <feature>"
|
||||
# • "Continue working on this change"
|
||||
#
|
||||
# Claude will automatically use the appropriate skill.
|
||||
#
|
||||
# 💡 This is an experimental feature.
|
||||
# Feedback welcome at: https://github.com/Fission-AI/OpenSpec/issues
|
||||
```
|
||||
|
||||
**Implementation Notes:**
|
||||
- Simple file writing: Create directories and write templated `SKILL.md` files (no complex logic)
|
||||
- Use existing `FileSystemUtils.writeFile()` pattern like slash command configurators
|
||||
- Template structure: YAML frontmatter + markdown body
|
||||
- Keep existing `/opsx:*` slash commands for now (manual cleanup later)
|
||||
- Skills use invocation model (user explicitly asks Claude to use them)
|
||||
- Skill `description` field guides when Claude suggests using the skill
|
||||
- Each `SKILL.md` has required fields: `name` (matches directory) and `description`
|
||||
|
||||
---
|
||||
|
||||
### 4. Update `/opsx:new` Command Content
|
||||
|
||||
**Current Behavior (awf:start):**
|
||||
1. Ask user what they want to build (if no input)
|
||||
2. Create change directory
|
||||
3. Show artifact status
|
||||
4. Show what's ready
|
||||
5. Get instructions for proposal
|
||||
6. STOP and wait
|
||||
|
||||
**New Behavior (opsx:new):**
|
||||
Same flow but with updated naming:
|
||||
- References to "awf" → "opsx"
|
||||
- References to `/awf:continue` → `/opsx:continue`
|
||||
- Update frontmatter name/description
|
||||
|
||||
**Tasks:**
|
||||
- [x] Update all "awf" references to "opsx"
|
||||
- [x] Update command references in prompt text
|
||||
- [x] Verify CLI commands still work (they use `openspec`, not `awf`)
|
||||
|
||||
---
|
||||
|
||||
### 5. Update `/opsx:continue` Command Content
|
||||
|
||||
**Current Behavior (awf:continue):**
|
||||
1. Prompt for change selection (if not provided)
|
||||
2. Check current status
|
||||
3. Create ONE artifact based on what's ready
|
||||
4. Show progress and what's unlocked
|
||||
5. STOP
|
||||
|
||||
**New Behavior (opsx:continue):**
|
||||
Same flow with updated naming.
|
||||
|
||||
**Tasks:**
|
||||
- [x] Update all "awf" references to "opsx"
|
||||
- [x] Update command references in prompt text
|
||||
|
||||
---
|
||||
|
||||
### 6. End-to-End Testing
|
||||
|
||||
**Objective:**
|
||||
Run through a complete workflow with Claude using the new skills to create a real feature, validating the entire flow works.
|
||||
|
||||
**Test Scenario:**
|
||||
Use a real OpenSpec feature as the test case (dog-fooding).
|
||||
|
||||
**Test Flow:**
|
||||
1. Run `openspec artifact-experimental-setup` to create skills
|
||||
2. Verify `.claude/skills/openspec-new-change/SKILL.md` created
|
||||
3. Verify `.claude/skills/openspec-continue-change/SKILL.md` created
|
||||
4. Verify `.claude/skills/openspec-apply-change/SKILL.md` created
|
||||
5. Ask Claude: "I want to start a new OpenSpec change to add feature X"
|
||||
6. Verify Claude invokes the `openspec-new-change` skill
|
||||
7. Verify change directory created at `openspec/changes/add-feature-x/`
|
||||
8. Verify proposal template shown
|
||||
9. Ask Claude: "Continue working on this change"
|
||||
10. Verify Claude invokes the `openspec-continue-change` skill
|
||||
11. Verify `proposal.md` created with content
|
||||
12. Ask Claude: "Continue" (create specs)
|
||||
13. Verify `specs/*.md` created
|
||||
14. Ask Claude: "Continue" (create design)
|
||||
15. Verify `design.md` created
|
||||
16. Ask Claude: "Continue" (create tasks)
|
||||
17. Verify `tasks.md` created
|
||||
18. Verify status shows 4/4 complete
|
||||
19. Implement the feature based on tasks
|
||||
20. Run `/openspec:archive` to archive the change
|
||||
|
||||
**Validation Checklist:**
|
||||
- [ ] `openspec artifact-experimental-setup` creates correct directory structure
|
||||
- [ ] Skills are auto-detected in Claude Code
|
||||
- [ ] Skill descriptions trigger appropriate invocations
|
||||
- [ ] Skills create change directory and show proposal template
|
||||
- [ ] Skills correctly identify ready artifacts
|
||||
- [ ] Skills create artifacts with meaningful content
|
||||
- [ ] Dependency detection works (specs requires proposal, etc.)
|
||||
- [ ] Progress tracking is accurate
|
||||
- [ ] Template content is useful and well-structured
|
||||
- [ ] Error handling works (invalid names, missing changes, etc.)
|
||||
- [ ] Works with different schemas (spec-driven, tdd)
|
||||
- [ ] Test in Cursor (Settings → Rules → Import Settings)
|
||||
|
||||
**Document Results:**
|
||||
- Create test log documenting what worked and what didn't
|
||||
- Note any friction points or confusing UX
|
||||
- Identify bugs or improvements needed before user release
|
||||
|
||||
---
|
||||
|
||||
### 7. Documentation for Users
|
||||
|
||||
**Create user-facing documentation explaining:**
|
||||
|
||||
1. **What is the experimental workflow?**
|
||||
- A new way to create OpenSpec changes step-by-step using Agent Skills
|
||||
- One artifact at a time with dependency tracking
|
||||
- More interactive and iterative than the batch approach
|
||||
- Works across Claude Code, Cursor, Windsurf, and other compatible editors
|
||||
|
||||
2. **How to set up experimental workflow**
|
||||
```bash
|
||||
openspec artifact-experimental-setup
|
||||
```
|
||||
|
||||
Note: This is a temporary command that will be integrated into `openspec init` once promoted to stable.
|
||||
|
||||
3. **Available skills**
|
||||
- `openspec-new-change` - Start a new change with artifact workflow
|
||||
- `openspec-continue-change` - Continue working (create next artifact)
|
||||
|
||||
4. **How to use**
|
||||
- **Claude Code**: Skills are auto-detected, just ask Claude naturally
|
||||
- "I want to start a new OpenSpec change to add X"
|
||||
- "Continue working on this change"
|
||||
- **Cursor**: Enable in Settings → Rules → Import Settings
|
||||
- **Windsurf**: Auto-imports `.claude` directory
|
||||
|
||||
5. **Example workflow**
|
||||
- Step-by-step walkthrough with natural language interactions
|
||||
- Show how Claude invokes skills based on user requests
|
||||
|
||||
6. **Feedback mechanism**
|
||||
- GitHub issue template for feedback
|
||||
- What to report (bugs, UX issues, suggestions)
|
||||
|
||||
**Tasks:**
|
||||
- [ ] Create `docs/experimental-workflow.md` user guide
|
||||
- [ ] Add GitHub issue template for experimental feedback
|
||||
- [ ] Update README with mention of experimental features
|
||||
|
||||
---
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
1. Remove WF skill files
|
||||
└── (no dependencies)
|
||||
|
||||
2. Rename AWF to OPSX
|
||||
└── (no dependencies)
|
||||
|
||||
3. Add Agent Skills
|
||||
└── Depends on: Rename AWF to OPSX (uses opsx content as templates)
|
||||
|
||||
4. Update opsx:new content
|
||||
└── Depends on: Rename AWF to OPSX
|
||||
|
||||
5. Update opsx:continue content
|
||||
└── Depends on: Rename AWF to OPSX
|
||||
|
||||
6. E2E Testing
|
||||
└── Depends on: Add Agent Skills (tests the skills workflow)
|
||||
|
||||
7. User Documentation
|
||||
└── Depends on: E2E Testing (need to know final behavior)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope
|
||||
|
||||
The following are explicitly NOT part of this experimental release:
|
||||
|
||||
1. **Batch mode refactor** - Making legacy `/openspec:proposal` use schemas
|
||||
2. **New schemas** - Only shipping with existing `spec-driven` and `tdd`
|
||||
3. **Schema customization UI** - No `openspec schema list` or similar
|
||||
4. **Multiple editor support in CLI** - Skills work cross-editor automatically via `.claude/skills/`
|
||||
5. **Replacing existing commands** - Skills are additive, not replacing `/openspec:*` or `/opsx:*`
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
The experimental release is ready when:
|
||||
|
||||
1. `openspec-new-change`, `openspec-continue-change`, and `openspec-apply-change` skills work end-to-end
|
||||
2. `openspec artifact-experimental-setup` creates skills in `.claude/skills/`
|
||||
3. Skills work in Claude Code and are compatible with Cursor/Windsurf
|
||||
4. At least one complete workflow has been tested manually
|
||||
5. User documentation exists explaining how to generate and use skills
|
||||
6. Feedback mechanism is in place
|
||||
7. WF skill files are removed
|
||||
8. No references to "awf" remain in user-facing content
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Schema selection** - Should `opsx:new` allow selecting a schema, or always use `spec-driven`?
|
||||
- Current: Always uses `spec-driven` as default
|
||||
- Consider: Add `--schema tdd` option or prompt
|
||||
|
||||
2. **Namespace in CLI** - Should experimental CLI commands be namespaced?
|
||||
- Current: `openspec status`, `openspec instructions` (no namespace)
|
||||
- Alternative: `openspec opsx status` (explicit experimental namespace)
|
||||
- Recommendation: Keep current, less typing for users
|
||||
|
||||
3. **Deprecation path** - If opsx becomes the default, how do we migrate?
|
||||
- Not needed for experimental release
|
||||
- Document that command names may change
|
||||
|
||||
---
|
||||
|
||||
## Estimated Work Breakdown
|
||||
|
||||
| Item | Complexity | Notes |
|
||||
|------|------------|-------|
|
||||
| Remove WF files | Trivial | Just delete 2 files + directory |
|
||||
| Rename AWF → OPSX | Low | File renames + content updates |
|
||||
| Add Agent Skills | **Low** | **Simple: 3-4 files, single output directory, standard format** |
|
||||
| Update opsx:new content | Low | Text replacements |
|
||||
| Update opsx:continue content | Low | Text replacements |
|
||||
| E2E Testing | Medium | Manual testing, documenting results |
|
||||
| User Documentation | Medium | New docs, issue template |
|
||||
|
||||
**Key Improvement:** Switching to Agent Skills reduces complexity significantly:
|
||||
- **Before:** 20+ files (type definitions, 18+ editor configurators, editor selection UI)
|
||||
- **After:** 3-4 files (skill templates, simple CLI command)
|
||||
- **Cross-editor:** Works automatically in Claude Code, Cursor, Windsurf without extra code
|
||||
|
||||
---
|
||||
|
||||
## User Feedback from E2E Testing
|
||||
|
||||
### What Worked Well
|
||||
|
||||
1. **Clear dependency graph** ⭐ HIGH PRIORITY - KEEP
|
||||
- The status command showing blocked/unblocked artifacts was intuitive:
|
||||
```
|
||||
[x] proposal
|
||||
[ ] design
|
||||
[-] tasks (blocked by: design, specs)
|
||||
```
|
||||
- Users always knew what they could work on next
|
||||
- **Relevance**: Core UX strength to preserve
|
||||
|
||||
2. **Structured instructions output** ⭐ HIGH PRIORITY - KEEP
|
||||
- `openspec instructions <artifact>` gave templates, output paths, and context in one call
|
||||
- Very helpful for understanding what to create
|
||||
- **Relevance**: Essential for agent-driven workflow
|
||||
|
||||
3. **Simple scaffolding** ✅ WORKS WELL
|
||||
- `openspec new change "name"` just worked - created directory structure without fuss
|
||||
- **Relevance**: Good baseline, room for improvement (see pain points)
|
||||
|
||||
---
|
||||
|
||||
### Pain Points & Confusion
|
||||
|
||||
1. **Redundant CLI calls** ⚠️ MEDIUM PRIORITY
|
||||
- Users called both `status` AND `next` every time, but they overlap significantly
|
||||
- `status` already shows what's blocked
|
||||
- **Recommendation**: Consider merging or making `next` give actionable guidance beyond just listing names
|
||||
- **Relevance**: Reduces friction in iterative workflow
|
||||
|
||||
2. **Specs directory structure was ambiguous** 🔥 HIGH PRIORITY - FIX
|
||||
- Instructions said: `Write to: .../specs/**/*.md`
|
||||
- Users had to guess: `specs/spec.md`? `specs/game/spec.md`? `specs/tic-tac-toe/spec.md`?
|
||||
- Users ended up doing manual `mkdir -p .../specs/tic-tac-toe` then writing `spec.md` inside
|
||||
- **Recommendation**: CLI should scaffold this directory structure automatically
|
||||
- **Relevance**: Critical agent UX - ambiguous paths cause workflow friction
|
||||
|
||||
3. **Repetitive --change flag** ⚠️ MEDIUM PRIORITY
|
||||
- Every command needed `--change "tic-tac-toe-game"`
|
||||
- After 10+ calls, this felt verbose
|
||||
- **Recommendation**: `openspec use "tic-tac-toe-game"` to set context, then subsequent commands assume that change
|
||||
- **Relevance**: Quality of life improvement for iterative sessions
|
||||
|
||||
4. **No validation feedback** 🔥 HIGH PRIORITY - ADD
|
||||
- After writing each artifact, users just ran `status` hoping it would show `[x]`
|
||||
- Questions raised:
|
||||
- How did it know the artifact was "done"? File existence?
|
||||
- What if spec format was wrong (e.g., wrong heading levels)?
|
||||
- **Recommendation**: Add `openspec validate --change "name"` to check content quality
|
||||
- **Relevance**: Critical for user confidence and catching errors early
|
||||
|
||||
5. **Query-heavy, action-light CLI** 🔥 HIGH PRIORITY - ENHANCE
|
||||
- Most commands retrieve info. The only "action" is `new change`
|
||||
- Artifact creation is manual Write to guessed paths
|
||||
- **Recommendation**: `openspec create proposal --change "name"` could scaffold the file with template pre-filled, then user just edits
|
||||
- **Relevance**: Directly impacts agent productivity - reduce manual file writing
|
||||
|
||||
6. **Instructions output was verbose** ⚠️ LOW PRIORITY
|
||||
- XML-style output (`<artifact>`, `<template>`, `<instruction>`) was parseable but long
|
||||
- Key info (output path, template) was buried in ~50 lines
|
||||
- **Recommendation**: Add compact mode or structured JSON output for agents
|
||||
- **Relevance**: Nice-to-have for agent parsing efficiency
|
||||
|
||||
---
|
||||
|
||||
### Workflow Friction
|
||||
|
||||
1. **Mandatory "STOP and wait" after showing proposal template** ⚠️ MEDIUM PRIORITY
|
||||
- The skill said "STOP and wait" after showing the proposal template
|
||||
- This felt overly cautious when user had already provided enough context (e.g., "tic tac toe, single player vs AI, minimal aesthetics")
|
||||
- **Recommendation**: Make the pause optional or conditional based on context clarity
|
||||
- **Relevance**: Reduces unnecessary round-trips in agent conversations
|
||||
|
||||
2. **No connection to implementation** 🔥 HIGH PRIORITY - ROADMAP ITEM
|
||||
- After 4/4 artifacts complete, then what? The workflow ends at planning
|
||||
- No `openspec apply` or guidance on how to execute the tasks
|
||||
- User asked "would you like me to implement?" but that's outside OpenSpec's scope currently
|
||||
- **Recommendation**: Add implementation bridge - either:
|
||||
- `openspec apply` command to start execution phase
|
||||
- Clear handoff to existing `/openspec:apply` workflow
|
||||
- Documentation on next steps after planning completes
|
||||
- **Relevance**: Critical missing piece - users expect end-to-end workflow
|
||||
|
||||
---
|
||||
|
||||
### Priority Summary
|
||||
|
||||
**MUST FIX (High Priority):**
|
||||
1. Specs directory structure ambiguity (#2)
|
||||
2. Add validation feedback (#4)
|
||||
3. Make CLI more action-oriented (#5)
|
||||
4. Bridge to implementation phase (#2 in Workflow Friction)
|
||||
5. Keep clear dependency graph (#1 in What Worked)
|
||||
6. Keep structured instructions (#2 in What Worked)
|
||||
|
||||
**SHOULD FIX (Medium Priority):**
|
||||
1. Reduce redundant CLI calls (#1)
|
||||
2. Repetitive `--change` flag (#3)
|
||||
3. Mandatory STOP behavior (#1 in Workflow Friction)
|
||||
|
||||
**NICE TO HAVE (Low Priority):**
|
||||
1. Compact instructions output mode (#6)
|
||||
|
||||
---
|
||||
|
||||
## Design Decisions (from E2E Testing Feedback)
|
||||
|
||||
Based on dev testing and analysis of agent workflow friction, we identified three blockers for experimental release and made the following decisions.
|
||||
|
||||
### Blockers Identified
|
||||
|
||||
From the pain points in E2E testing, three issues are blocking the experimental release:
|
||||
|
||||
1. **Specs directory ambiguity** - Agents don't know where to write spec files or how to name capabilities
|
||||
2. **CLI is query-heavy** - Most commands retrieve info, artifact creation is manual
|
||||
3. **Apply integration missing** - After 4/4 artifacts complete, no guidance on implementation phase
|
||||
|
||||
### Decision 1: Capability Discovery in Proposal (RESOLVED)
|
||||
|
||||
**Problem:** The specs artifact instruction says "Create one spec file per capability in `specs/<name>/spec.md`" but:
|
||||
- Agent doesn't know what `<name>` should be
|
||||
- Capability identification requires research (existing specs, codebase)
|
||||
- Proposal template asks for "Affected specs" but doesn't structure it
|
||||
- Research happens implicitly, output isn't captured
|
||||
|
||||
**Decision:** Enrich the proposal template to explicitly capture capability discovery.
|
||||
|
||||
**Current proposal template:**
|
||||
```markdown
|
||||
## Why
|
||||
## What Changes
|
||||
## Impact
|
||||
- Affected specs: List capabilities... ← vague, easy to skip
|
||||
- Affected code: ...
|
||||
```
|
||||
|
||||
**New proposal template:**
|
||||
```markdown
|
||||
## Why
|
||||
## What Changes
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
<!-- Capabilities being introduced (will create new specs/<name>/spec.md) -->
|
||||
- `<name>`: <brief description of what this capability covers>
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- Existing capabilities being changed (will update existing specs) -->
|
||||
- `<existing-name>`: <what's changing>
|
||||
|
||||
## Impact
|
||||
<!-- Affected code, APIs, dependencies, systems -->
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- Proposal already asks for capabilities (just poorly) - this makes it explicit
|
||||
- Captured output is reviewable (vs implicit research that can't be verified)
|
||||
- Creates clear contract between proposal and specs phases
|
||||
- Distinguishes NEW vs MODIFIED upfront (critical for specs phase)
|
||||
- Agent can't skip research - it's part of the deliverable
|
||||
|
||||
**Implementation:**
|
||||
- Update `schemas/spec-driven/templates/proposal.md`
|
||||
- Update proposal instruction in `schemas/spec-driven/schema.yaml`
|
||||
- Update skill instructions to guide capability discovery
|
||||
|
||||
### Decision 2: CLI Action Commands (IN PROGRESS)
|
||||
|
||||
**Problem:** CLI is mostly query-oriented. Agents run `openspec status`, `openspec next`, `openspec instructions` but then must manually write files.
|
||||
|
||||
#### Decision 2a: Remove `openspec next` command (RESOLVED)
|
||||
|
||||
**Problem:** The `next` command is redundant. It only shows which artifacts are ready, but `status` already shows this information (artifacts with status "ready" vs "blocked" vs "done").
|
||||
|
||||
**Current behavior:**
|
||||
```bash
|
||||
openspec status --change "X" # Shows: proposal (done), specs (ready), design (blocked), tasks (blocked)
|
||||
openspec next --change "X" # Shows: ["specs"] ← redundant
|
||||
```
|
||||
|
||||
**Decision:** Remove the `next` command. Agents should use `status` which provides the same info plus more context.
|
||||
|
||||
**Implementation:**
|
||||
- Remove `next` command from CLI
|
||||
- Update skill instructions to use `status` instead of `next`
|
||||
- Update AGENTS.md references
|
||||
|
||||
#### Decision 2b: CLI Scaffolding (RESOLVED - NO)
|
||||
|
||||
**Problem:** After getting instructions, agents manually write files. Should CLI scaffold artifacts instead?
|
||||
|
||||
**Options considered:**
|
||||
- Add `openspec create <artifact>` commands that scaffold files with templates
|
||||
- Keep current approach where agent writes files directly from instructions
|
||||
- Hybrid: CLI can scaffold, agent can also write directly
|
||||
|
||||
**Decision:** Keep current flow. No scaffolding commands.
|
||||
|
||||
**Rationale (from agent ergonomics perspective):**
|
||||
- One Write is better than multiple Edits - agent composes full content atomically
|
||||
- `instructions` already provides template in context - scaffolding just moves it to a file
|
||||
- Fewer tool calls: `instructions` + Write (2) vs `create` + `instructions` + Read + Edit×N (4+)
|
||||
- Scaffolding doesn't solve the real problem (not knowing WHAT to write)
|
||||
- Real problem solved by proposal template change (capability discovery)
|
||||
|
||||
**For multi-file artifacts (specs):** Scaffolding can't help because CLI doesn't know capability names until proposal is complete. The capability discovery in proposal solves this.
|
||||
|
||||
### Decision 3: Apply Integration (RESOLVED)
|
||||
|
||||
**Original problem:** After planning completes (4/4 artifacts), the experimental workflow ends. No guidance on implementation.
|
||||
|
||||
**Key insight: No phases, just actions.**
|
||||
|
||||
Through discussion, we realized phases (planning → implementation → archive) are an artificial constraint. Work is fluid:
|
||||
- You might start implementing, realize the design is wrong → update design.md
|
||||
- You're halfway through tasks, discover a new requirement → update specs
|
||||
- You bounce between "planning" and "implementing" constantly
|
||||
|
||||
**The better model: Actions on a Change**
|
||||
|
||||
A change is a thing (with artifacts). Actions are verbs you perform on a change. Actions aren't phases - they're fluid operations you can perform anytime.
|
||||
|
||||
| Action | What it does | Skill | CLI Command |
|
||||
|--------|--------------|-------|-------------|
|
||||
| `new` | Create a change (scaffold directory) | `opsx:new` | `openspec new change` |
|
||||
| `continue` | Create next artifact (dependency-aware) | `opsx:continue` | `openspec instructions` |
|
||||
| `apply` | Implement tasks (execute, check off) | `opsx:apply` (NEW) | TBD |
|
||||
| `update` | Refresh/update artifacts based on learnings | `opsx:update` (NEW) | TBD |
|
||||
| `explore` | Research, ask questions, understand | `opsx:explore` (NEW) | TBD |
|
||||
| `validate` | Check artifacts are correct/complete | TBD | `openspec validate` |
|
||||
| `archive` | Finalize and move to archive | existing | `openspec archive` |
|
||||
|
||||
**Key principles:**
|
||||
- Actions are modeled as skills (primary interface for agents)
|
||||
- Some skills have matching CLI commands for convenience
|
||||
- Skills and CLI commands are decoupled - not everything needs both
|
||||
- Actions can be performed in any order (with soft prerequisites)
|
||||
- No linear phase gates
|
||||
|
||||
**What the schema defines:**
|
||||
- Artifacts (what they are, where they go)
|
||||
- Dependencies (what must exist first)
|
||||
- Required vs optional
|
||||
- Templates + instructions
|
||||
|
||||
**What the schema does NOT define:**
|
||||
- Phases
|
||||
- When you can modify things
|
||||
- Linear workflow
|
||||
|
||||
**Progress tracking:**
|
||||
- tasks.md checkboxes = implementation progress
|
||||
- Artifact existence = planning progress
|
||||
- Archive readiness = user decides (or all tasks done)
|
||||
|
||||
**For experimental release:**
|
||||
- Create `opsx:apply` skill (guidance for implementing tasks)
|
||||
- Document the "actions on a change" model
|
||||
- Other actions (update, explore) can come later
|
||||
|
||||
---
|
||||
|
||||
### Design: `openspec-apply-change` Skill
|
||||
|
||||
#### Overview
|
||||
|
||||
The apply skill guides agents through implementing tasks from a completed (or in-progress) change. Unlike the old `/openspec:apply` command, this skill:
|
||||
- Is **fluid** - can be invoked anytime, not just after all artifacts are done
|
||||
- Allows **artifact updates** - if implementation reveals issues, update design/specs
|
||||
- Works **until done** - keeps going through tasks until complete or blocked
|
||||
- Tracks **progress via checkboxes** - tasks.md is the source of truth
|
||||
|
||||
#### Skill Metadata
|
||||
|
||||
```yaml
|
||||
name: openspec-apply-change
|
||||
description: Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.
|
||||
```
|
||||
|
||||
#### When to Invoke
|
||||
|
||||
The skill should be invoked when:
|
||||
- User says "implement this change" or "start implementing"
|
||||
- User says "work on the tasks" or "do the next task"
|
||||
- User says "apply this change"
|
||||
- All artifacts are complete and user wants to proceed
|
||||
- User wants to continue implementation after a break
|
||||
|
||||
#### Input
|
||||
|
||||
- Optionally: change name
|
||||
- Optionally: specific task number to work on
|
||||
- If omitted: prompt for change selection (same pattern as continue-change)
|
||||
|
||||
#### Steps
|
||||
|
||||
```markdown
|
||||
**Steps**
|
||||
|
||||
1. **If no change name provided, prompt for selection**
|
||||
|
||||
Run `openspec list --json` to get available changes. Use **AskUserQuestion** to let user select.
|
||||
|
||||
Show changes that have tasks.md (implementation-ready).
|
||||
Mark changes with incomplete tasks as "(In Progress)".
|
||||
|
||||
2. **Get apply instructions**
|
||||
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" --json
|
||||
```
|
||||
|
||||
This returns:
|
||||
- Context file paths (proposal, specs, design, tasks)
|
||||
- Progress (total, complete, remaining)
|
||||
- Task list with status
|
||||
- Dynamic instruction based on current state
|
||||
|
||||
**Handle states:**
|
||||
- If blocked (missing artifacts): show message, suggest `openspec-continue-change`
|
||||
- If all done: congratulate, suggest archive
|
||||
- Otherwise: proceed to implementation
|
||||
|
||||
3. **Read context files**
|
||||
|
||||
Read the files listed in the instructions:
|
||||
- `proposal.md` - why and what
|
||||
- `specs/*.md` - requirements and scenarios
|
||||
- `design.md` - technical approach (if exists)
|
||||
- `tasks.md` - the implementation checklist
|
||||
|
||||
4. **Show current progress**
|
||||
|
||||
Display:
|
||||
- Progress: "N/M tasks complete"
|
||||
- Remaining tasks overview
|
||||
- Dynamic instruction from CLI
|
||||
|
||||
5. **Implement tasks (loop until done or blocked)**
|
||||
|
||||
For each pending task:
|
||||
- Show which task is being worked on
|
||||
- Make the code changes required
|
||||
- Keep changes minimal and focused
|
||||
- Mark task complete in tasks.md: `- [ ]` → `- [x]`
|
||||
- Continue to next task
|
||||
|
||||
**Pause if:**
|
||||
- Task is unclear → ask for clarification
|
||||
- Implementation reveals a design issue → suggest updating artifacts
|
||||
- Error or blocker encountered → report and wait for guidance
|
||||
- User interrupts
|
||||
|
||||
6. **On completion or pause, show status**
|
||||
|
||||
Display:
|
||||
- Tasks completed this session
|
||||
- Overall progress: "N/M tasks complete"
|
||||
- If all done: suggest archive
|
||||
- If paused: explain why and wait for guidance
|
||||
```
|
||||
|
||||
#### Output Format
|
||||
|
||||
**During implementation:**
|
||||
```
|
||||
## Implementing: add-user-auth
|
||||
|
||||
Working on task 3/7: Create UserAuth service class
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 4/7: Add login endpoint to AuthController
|
||||
[...implementation happening...]
|
||||
✓ Task complete
|
||||
|
||||
Working on task 5/7: Add JWT token generation
|
||||
[...implementation happening...]
|
||||
```
|
||||
|
||||
**On completion:**
|
||||
```
|
||||
## Implementation Complete
|
||||
|
||||
**Change:** add-user-auth
|
||||
**Progress:** 7/7 tasks complete ✓
|
||||
|
||||
### Completed This Session
|
||||
- [x] Create UserAuth service class
|
||||
- [x] Add login endpoint to AuthController
|
||||
- [x] Add JWT token generation
|
||||
- [x] Add logout endpoint
|
||||
- [x] Add auth middleware
|
||||
- [x] Write unit tests
|
||||
- [x] Update API documentation
|
||||
|
||||
All tasks complete! Ready to archive this change.
|
||||
```
|
||||
|
||||
**On pause (issue encountered):**
|
||||
```
|
||||
## Implementation Paused
|
||||
|
||||
**Change:** add-user-auth
|
||||
**Progress:** 4/7 tasks complete
|
||||
|
||||
### Issue Encountered
|
||||
Task 5 "Add JWT token generation" - the design specifies using RS256 but
|
||||
the existing auth library only supports HS256.
|
||||
|
||||
**Options:**
|
||||
1. Update design.md to use HS256 instead
|
||||
2. Add a new JWT library that supports RS256
|
||||
3. Other approach
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
#### Guardrails
|
||||
|
||||
- Keep going through tasks until done or blocked
|
||||
- Always read context before starting (specs, design)
|
||||
- If task is ambiguous, pause and ask before implementing
|
||||
- If implementation reveals issues, pause and suggest artifact updates
|
||||
- Keep code changes minimal and scoped to each task
|
||||
- Update task checkbox immediately after completing each task
|
||||
- Pause on errors, blockers, or unclear requirements - don't guess
|
||||
|
||||
#### Fluid Workflow Integration
|
||||
|
||||
The apply skill supports the "actions on a change" model:
|
||||
|
||||
**Can be invoked anytime:**
|
||||
- Before all artifacts are done (if tasks.md exists)
|
||||
- After partial implementation
|
||||
- Interleaved with other actions (update, continue)
|
||||
|
||||
**Allows artifact updates:**
|
||||
- If implementation reveals design issues → suggest `opsx:update` or manual edit
|
||||
- If requirements need clarification → suggest updating specs
|
||||
- Not phase-locked - work fluidly
|
||||
|
||||
**Example fluid workflow:**
|
||||
```
|
||||
User: "Implement add-user-auth"
|
||||
→ openspec-apply-change: implements tasks 1, 2, 3, 4...
|
||||
→ Pauses at task 5: "Design says RS256 but library only supports HS256"
|
||||
|
||||
User: "Let's use HS256 instead, update the design"
|
||||
→ User edits design.md (or uses opsx:update in future)
|
||||
|
||||
User: "Continue implementing"
|
||||
→ openspec-apply-change: implements tasks 5, 6, 7
|
||||
→ "All tasks complete! Ready to archive."
|
||||
```
|
||||
|
||||
#### CLI Commands Used
|
||||
|
||||
```bash
|
||||
openspec list --json # List changes for selection
|
||||
openspec status --change "<name>" # Check artifact completion
|
||||
openspec instructions apply --change "<name>" # Get apply instructions (NEW)
|
||||
# File reads via Read tool for proposal, specs, design, tasks
|
||||
# File edits via Edit tool for checking off tasks
|
||||
```
|
||||
|
||||
#### New CLI Command: `openspec instructions apply`
|
||||
|
||||
For consistency with artifact instructions.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
openspec instructions apply --change "<name>" [--json]
|
||||
```
|
||||
|
||||
**Output (Markdown format):**
|
||||
```markdown
|
||||
## Apply: add-user-auth
|
||||
|
||||
### Context Files
|
||||
- proposal: openspec/changes/add-user-auth/proposal.md
|
||||
- specs: openspec/changes/add-user-auth/specs/**/*.md
|
||||
- design: openspec/changes/add-user-auth/design.md
|
||||
- tasks: openspec/changes/add-user-auth/tasks.md
|
||||
|
||||
### Progress
|
||||
2/7 complete
|
||||
|
||||
### Tasks
|
||||
- [x] Create UserAuth service class
|
||||
- [x] Add login endpoint
|
||||
- [ ] Add JWT token generation
|
||||
- [ ] Add logout endpoint
|
||||
- [ ] Add auth middleware
|
||||
- [ ] Write unit tests
|
||||
- [ ] Update API documentation
|
||||
|
||||
### Instruction
|
||||
Read context files, work through pending tasks, mark complete as you go.
|
||||
Pause if you hit blockers or need clarification.
|
||||
```
|
||||
|
||||
**Benefits of CLI command:**
|
||||
- **Consistency** - same pattern as `openspec instructions <artifact>`
|
||||
- **Structured output** - progress, tasks, context paths in one call
|
||||
- **Clean format** - markdown is readable and compact (vs verbose XML)
|
||||
- **Extensibility** - can add more sections later if needed
|
||||
- **JSON option** - `--json` flag available for programmatic use
|
||||
|
||||
#### Differences from Old `/openspec:apply`
|
||||
|
||||
| Aspect | Old `/openspec:apply` | New `openspec-apply-change` |
|
||||
|--------|----------------------|----------------------------|
|
||||
| Invocation | After all artifacts done | Anytime (if tasks.md exists) |
|
||||
| Granularity | All tasks at once | All tasks, but pauses on issues |
|
||||
| Artifact updates | Not mentioned | Encouraged when needed |
|
||||
| Progress tracking | Update all at end | Update after each task |
|
||||
| Flow control | Push through everything | Pause on blockers, resume after |
|
||||
| Context loading | Read once at start | Read context, reference as needed |
|
||||
| Issue handling | Not specified | Pause, present options, wait for guidance |
|
||||
|
||||
#### Implementation Notes
|
||||
|
||||
1. **Add CLI command**: Add `openspec instructions apply` to artifact-workflow.ts
|
||||
- Parse tasks.md for progress (count done/pending)
|
||||
- Return context paths, progress, task list, simple instruction
|
||||
2. **Add to skill-templates.ts**: Create `getApplyChangeSkillTemplate()` function
|
||||
3. **Update artifact-experimental-setup**: Generate this skill alongside new/continue
|
||||
4. **Update skills list**: Add to `.claude/skills/` directory
|
||||
5. **Test the flow**: Verify it works with existing changes that have tasks.md
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ~~Review this plan and confirm scope~~ (Done - blockers identified)
|
||||
2. ~~Design decisions~~ (Done - all 3 blockers resolved)
|
||||
3. ~~Design apply skill~~ (Done - documented above)
|
||||
4. ~~Implement proposal template change (Decision 1 - capability discovery)~~ (Done)
|
||||
5. ~~Remove `openspec next` command (Decision 2a)~~ (Done)
|
||||
6. ~~Add `openspec instructions apply` CLI command~~ (Done)
|
||||
7. ~~Create `openspec-apply-change` skill~~ (Done)
|
||||
8. Conduct E2E testing with updated workflow
|
||||
9. Write user docs (document "actions on a change" model)
|
||||
10. Release to test users
|
||||
@@ -0,0 +1,273 @@
|
||||
# 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).
|
||||
|
||||
## 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:
|
||||
|
||||
```
|
||||
┌────────────────────┐
|
||||
│ 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 │
|
||||
└────────────────────┘
|
||||
```
|
||||
|
||||
## What OpenSpec Creates
|
||||
|
||||
After running `openspec init`, your project has this structure:
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── specs/ # Source of truth (your system's behavior)
|
||||
│ └── <domain>/
|
||||
│ └── spec.md
|
||||
├── changes/ # Proposed updates (one folder per change)
|
||||
│ └── <change-name>/
|
||||
│ ├── proposal.md
|
||||
│ ├── design.md
|
||||
│ ├── tasks.md
|
||||
│ └── specs/ # Delta specs (what's changing)
|
||||
│ └── <domain>/
|
||||
│ └── spec.md
|
||||
└── config.yaml # Project configuration (optional)
|
||||
```
|
||||
|
||||
**Two key directories:**
|
||||
|
||||
- **`specs/`** - The source of truth. These specs describe how your system currently behaves. Organized by domain (e.g., `specs/auth/`, `specs/payments/`).
|
||||
|
||||
- **`changes/`** - Proposed modifications. Each change gets its own folder with all related artifacts. When a change is complete, its specs merge into the main `specs/` directory.
|
||||
|
||||
## Understanding Artifacts
|
||||
|
||||
Each change folder contains artifacts that guide the work:
|
||||
|
||||
| Artifact | Purpose |
|
||||
|----------|---------|
|
||||
| `proposal.md` | The "why" and "what" - captures intent, scope, and approach |
|
||||
| `specs/` | Delta specs showing ADDED/MODIFIED/REMOVED requirements |
|
||||
| `design.md` | The "how" - technical approach and architecture decisions |
|
||||
| `tasks.md` | Implementation checklist with checkboxes |
|
||||
|
||||
**Artifacts build on each other:**
|
||||
|
||||
```
|
||||
proposal ──► specs ──► design ──► tasks ──► implement
|
||||
▲ ▲ ▲ │
|
||||
└───────────┴──────────┴────────────────────┘
|
||||
update as you learn
|
||||
```
|
||||
|
||||
You can always go back and refine earlier artifacts as you learn more during implementation.
|
||||
|
||||
## How Delta Specs Work
|
||||
|
||||
Delta specs are the key concept in OpenSpec. They show what's changing relative to your current specs.
|
||||
|
||||
### The Format
|
||||
|
||||
Delta specs use sections to indicate the type of change:
|
||||
|
||||
```markdown
|
||||
# Delta for Auth
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Two-Factor Authentication
|
||||
The system MUST require a second factor during login.
|
||||
|
||||
#### Scenario: OTP required
|
||||
- GIVEN a user with 2FA enabled
|
||||
- WHEN the user submits valid credentials
|
||||
- THEN an OTP challenge is presented
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Session Timeout
|
||||
The system SHALL expire sessions after 30 minutes of inactivity.
|
||||
(Previously: 60 minutes)
|
||||
|
||||
#### Scenario: Idle timeout
|
||||
- GIVEN an authenticated session
|
||||
- WHEN 30 minutes pass without activity
|
||||
- THEN the session is invalidated
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Remember Me
|
||||
(Deprecated in favor of 2FA)
|
||||
```
|
||||
|
||||
### What Happens on Archive
|
||||
|
||||
When you archive a change:
|
||||
|
||||
1. **ADDED** requirements are appended to the main spec
|
||||
2. **MODIFIED** requirements replace the existing version
|
||||
3. **REMOVED** requirements are deleted from the main spec
|
||||
|
||||
The change folder moves to `openspec/changes/archive/` for audit history.
|
||||
|
||||
## Example: Your First Change
|
||||
|
||||
Let's walk through adding dark mode to an application.
|
||||
|
||||
### 1. Start the Change
|
||||
|
||||
```
|
||||
You: /opsx:new 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
|
||||
✓ tasks.md — implementation checklist
|
||||
Ready for implementation!
|
||||
```
|
||||
|
||||
### 3. What Gets Created
|
||||
|
||||
**proposal.md** - Captures the intent:
|
||||
|
||||
```markdown
|
||||
# Proposal: Add Dark Mode
|
||||
|
||||
## Intent
|
||||
Users have requested a dark mode option to reduce eye strain
|
||||
during nighttime usage.
|
||||
|
||||
## Scope
|
||||
- Add theme toggle in settings
|
||||
- Support system preference detection
|
||||
- Persist preference in localStorage
|
||||
|
||||
## Approach
|
||||
Use CSS custom properties for theming with a React context
|
||||
for state management.
|
||||
```
|
||||
|
||||
**specs/ui/spec.md** - Delta showing new requirements:
|
||||
|
||||
```markdown
|
||||
# Delta for UI
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Theme Selection
|
||||
The system SHALL allow users to choose between light and dark themes.
|
||||
|
||||
#### Scenario: Manual toggle
|
||||
- GIVEN a user on any page
|
||||
- WHEN the user clicks the theme toggle
|
||||
- THEN the theme switches immediately
|
||||
- AND the preference persists across sessions
|
||||
|
||||
#### Scenario: System preference
|
||||
- GIVEN a user with no saved preference
|
||||
- WHEN the application loads
|
||||
- THEN the system's preferred color scheme is used
|
||||
```
|
||||
|
||||
**tasks.md** - Implementation checklist:
|
||||
|
||||
```markdown
|
||||
# Tasks
|
||||
|
||||
## 1. Theme Infrastructure
|
||||
- [ ] 1.1 Create ThemeContext with light/dark state
|
||||
- [ ] 1.2 Add CSS custom properties for colors
|
||||
- [ ] 1.3 Implement localStorage persistence
|
||||
|
||||
## 2. UI Components
|
||||
- [ ] 2.1 Create ThemeToggle component
|
||||
- [ ] 2.2 Add toggle to settings page
|
||||
- [ ] 2.3 Update Header to include quick toggle
|
||||
|
||||
## 3. Styling
|
||||
- [ ] 3.1 Define dark theme color palette
|
||||
- [ ] 3.2 Update components to use CSS variables
|
||||
```
|
||||
|
||||
### 4. Implement
|
||||
|
||||
```
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Working through tasks...
|
||||
✓ 1.1 Created ThemeContext with light/dark state
|
||||
✓ 1.2 Added CSS custom properties to globals.css
|
||||
✓ 1.3 Implemented localStorage persistence
|
||||
✓ 2.1 Created ThemeToggle component
|
||||
...
|
||||
All tasks complete!
|
||||
```
|
||||
|
||||
During implementation, if you discover the design needs adjustment, just update the artifact and continue.
|
||||
|
||||
### 5. Archive
|
||||
|
||||
```
|
||||
You: /opsx:archive
|
||||
|
||||
AI: Archiving add-dark-mode...
|
||||
✓ Merged specs into openspec/specs/ui/spec.md
|
||||
✓ Moved to openspec/changes/archive/2025-01-24-add-dark-mode/
|
||||
Done! Ready for the next feature.
|
||||
```
|
||||
|
||||
Your delta specs are now part of the main specs, documenting how your system works.
|
||||
|
||||
## Verifying and Reviewing
|
||||
|
||||
Use the CLI to check on your changes:
|
||||
|
||||
```bash
|
||||
# List active changes
|
||||
openspec list
|
||||
|
||||
# View change details
|
||||
openspec show add-dark-mode
|
||||
|
||||
# Validate spec formatting
|
||||
openspec validate add-dark-mode
|
||||
|
||||
# Interactive dashboard
|
||||
openspec view
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [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
|
||||
@@ -0,0 +1,79 @@
|
||||
# Installation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Node.js 20.19.0 or higher** — Check your version: `node --version`
|
||||
|
||||
## Package Managers
|
||||
|
||||
### npm
|
||||
|
||||
```bash
|
||||
npm install -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
### pnpm
|
||||
|
||||
```bash
|
||||
pnpm add -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
### yarn
|
||||
|
||||
```bash
|
||||
yarn global add @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
### bun
|
||||
|
||||
```bash
|
||||
bun add -g @fission-ai/openspec@latest
|
||||
```
|
||||
|
||||
## Nix
|
||||
|
||||
Run OpenSpec directly without installation:
|
||||
|
||||
```bash
|
||||
nix run github:Fission-AI/OpenSpec -- init
|
||||
```
|
||||
|
||||
Or install to your profile:
|
||||
|
||||
```bash
|
||||
nix profile install github:Fission-AI/OpenSpec
|
||||
```
|
||||
|
||||
Or add to your development environment in `flake.nix`:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
openspec.url = "github:Fission-AI/OpenSpec";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, openspec, ... }: {
|
||||
devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
buildInputs = [ openspec.packages.x86_64-linux.default ];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Verify Installation
|
||||
|
||||
```bash
|
||||
openspec --version
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After installing, initialize OpenSpec in your project:
|
||||
|
||||
```bash
|
||||
cd your-project
|
||||
openspec init
|
||||
```
|
||||
|
||||
See [Getting Started](getting-started.md) for a full walkthrough.
|
||||
@@ -0,0 +1,575 @@
|
||||
# Migrating to OPSX
|
||||
|
||||
This guide helps you transition from the legacy OpenSpec workflow to OPSX. The migration is designed to be smooth—your existing work is preserved, and the new system offers more flexibility.
|
||||
|
||||
## What's Changing?
|
||||
|
||||
OPSX replaces the old phase-locked workflow with a fluid, action-based approach. Here's the key shift:
|
||||
|
||||
| Aspect | Legacy | OPSX |
|
||||
|--------|--------|------|
|
||||
| **Commands** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` | `/opsx:new`, `/opsx:continue`, `/opsx:apply`, and more |
|
||||
| **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 |
|
||||
| **Configuration** | `CLAUDE.md` with markers + `project.md` | Clean config in `openspec/config.yaml` |
|
||||
|
||||
**The philosophy change:** Work isn't linear. OPSX stops pretending it is.
|
||||
|
||||
---
|
||||
|
||||
## Before You Begin
|
||||
|
||||
### Your Existing Work Is Safe
|
||||
|
||||
The migration process is designed with preservation in mind:
|
||||
|
||||
- **Active changes in `openspec/changes/`** — Completely preserved. You can continue them with OPSX commands.
|
||||
- **Archived changes** — Untouched. Your history remains intact.
|
||||
- **Main specs in `openspec/specs/`** — Untouched. These are your source of truth.
|
||||
- **Your content in CLAUDE.md, AGENTS.md, etc.** — Preserved. Only the OpenSpec marker blocks are removed; everything you wrote stays.
|
||||
|
||||
### What Gets Removed
|
||||
|
||||
Only OpenSpec-managed files that are being replaced:
|
||||
|
||||
| What | Why |
|
||||
|------|-----|
|
||||
| Legacy slash command directories/files | Replaced by the new skills system |
|
||||
| `openspec/AGENTS.md` | Obsolete workflow trigger |
|
||||
| OpenSpec markers in `CLAUDE.md`, `AGENTS.md`, etc. | No longer needed |
|
||||
|
||||
**Legacy command locations by tool** (examples—your tool may vary):
|
||||
|
||||
- Claude Code: `.claude/commands/openspec/`
|
||||
- Cursor: `.cursor/commands/openspec-*.md`
|
||||
- Windsurf: `.windsurf/workflows/openspec-*.md`
|
||||
- Cline: `.clinerules/workflows/openspec-*.md`
|
||||
- Roo: `.roo/commands/openspec-*.md`
|
||||
- GitHub Copilot: `.github/prompts/openspec-*.prompt.md`
|
||||
- And others (Augment, Continue, Amazon Q, etc.)
|
||||
|
||||
The migration detects whichever tools you have configured and cleans up their legacy files.
|
||||
|
||||
The removal list may seem long, but these are all files that OpenSpec originally created. Your own content is never deleted.
|
||||
|
||||
### What Needs Your Attention
|
||||
|
||||
One file requires manual migration:
|
||||
|
||||
**`openspec/project.md`** — This file isn't deleted automatically because it may contain project context you've written. You'll need to:
|
||||
|
||||
1. Review its contents
|
||||
2. Move useful context to `openspec/config.yaml` (see guidance below)
|
||||
3. Delete the file when ready
|
||||
|
||||
**Why we made this change:**
|
||||
|
||||
The old `project.md` was passive—agents might read it, might not, might forget what they read. We found reliability was inconsistent.
|
||||
|
||||
The new `config.yaml` context is **actively injected into every OpenSpec planning request**. This means your project conventions, tech stack, and rules are always present when the AI is creating artifacts. Higher reliability.
|
||||
|
||||
**The tradeoff:**
|
||||
|
||||
Because context is injected into every request, you'll want to be concise. Focus on what really matters:
|
||||
- Tech stack and key conventions
|
||||
- Non-obvious constraints the AI needs to know
|
||||
- Rules that frequently got ignored before
|
||||
|
||||
Don't worry about getting it perfect. We're still learning what works best here, and we'll be improving how context injection works as we experiment.
|
||||
|
||||
---
|
||||
|
||||
## Running the Migration
|
||||
|
||||
Both `openspec init` and `openspec update` detect legacy files and guide you through the same cleanup process. Use whichever fits your situation:
|
||||
|
||||
### Using `openspec init`
|
||||
|
||||
Run this if you want to add new tools or reconfigure which tools are set up:
|
||||
|
||||
```bash
|
||||
openspec init
|
||||
```
|
||||
|
||||
The init command detects legacy files and guides you through cleanup:
|
||||
|
||||
```
|
||||
Upgrading to the new OpenSpec
|
||||
|
||||
OpenSpec now uses agent skills, the emerging standard across coding
|
||||
agents. This simplifies your setup while keeping everything working
|
||||
as before.
|
||||
|
||||
Files to remove
|
||||
No user content to preserve:
|
||||
• .claude/commands/openspec/
|
||||
• openspec/AGENTS.md
|
||||
|
||||
Files to update
|
||||
OpenSpec markers will be removed, your content preserved:
|
||||
• CLAUDE.md
|
||||
• AGENTS.md
|
||||
|
||||
Needs your attention
|
||||
• openspec/project.md
|
||||
We won't delete this file. It may contain useful project context.
|
||||
|
||||
The new openspec/config.yaml has a "context:" section for planning
|
||||
context. This is included in every OpenSpec request and works more
|
||||
reliably than the old project.md approach.
|
||||
|
||||
Review project.md, move any useful content to config.yaml's context
|
||||
section, then delete the file when ready.
|
||||
|
||||
? Upgrade and clean up legacy files? (Y/n)
|
||||
```
|
||||
|
||||
**What happens when you say yes:**
|
||||
|
||||
1. Legacy slash command directories are removed
|
||||
2. OpenSpec markers are stripped from `CLAUDE.md`, `AGENTS.md`, etc. (your content stays)
|
||||
3. `openspec/AGENTS.md` is deleted
|
||||
4. New skills are installed in `.claude/skills/`
|
||||
5. `openspec/config.yaml` is created with a default schema
|
||||
|
||||
### Using `openspec update`
|
||||
|
||||
Run this if you just want to migrate and refresh your existing tools to the latest version:
|
||||
|
||||
```bash
|
||||
openspec update
|
||||
```
|
||||
|
||||
The update command also detects and cleans up legacy artifacts, then refreshes your skills to the latest version.
|
||||
|
||||
### Non-Interactive / CI Environments
|
||||
|
||||
For scripted migrations:
|
||||
|
||||
```bash
|
||||
openspec init --force --tools claude
|
||||
```
|
||||
|
||||
The `--force` flag skips prompts and auto-accepts cleanup.
|
||||
|
||||
---
|
||||
|
||||
## Migrating project.md to config.yaml
|
||||
|
||||
The old `openspec/project.md` was a freeform markdown file for project context. The new `openspec/config.yaml` is structured and—critically—**injected into every planning request** so your conventions are always present when the AI works.
|
||||
|
||||
### Before (project.md)
|
||||
|
||||
```markdown
|
||||
# Project Context
|
||||
|
||||
This is a TypeScript monorepo using React and Node.js.
|
||||
We use Jest for testing and follow strict ESLint rules.
|
||||
Our API is RESTful and documented in docs/api.md.
|
||||
|
||||
## Conventions
|
||||
|
||||
- All public APIs must maintain backwards compatibility
|
||||
- New features should include tests
|
||||
- Use Given/When/Then format for specifications
|
||||
```
|
||||
|
||||
### After (config.yaml)
|
||||
|
||||
```yaml
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Tech stack: TypeScript, React, Node.js
|
||||
Testing: Jest with React Testing Library
|
||||
API: RESTful, documented in docs/api.md
|
||||
We maintain backwards compatibility for all public APIs
|
||||
|
||||
rules:
|
||||
proposal:
|
||||
- Include rollback plan for risky changes
|
||||
specs:
|
||||
- Use Given/When/Then format for scenarios
|
||||
- Reference existing patterns before inventing new ones
|
||||
design:
|
||||
- Include sequence diagrams for complex flows
|
||||
```
|
||||
|
||||
### Key Differences
|
||||
|
||||
| project.md | config.yaml |
|
||||
|------------|-------------|
|
||||
| Freeform markdown | Structured YAML |
|
||||
| One blob of text | Separate context and per-artifact rules |
|
||||
| Unclear when it's used | Context appears in ALL artifacts; rules appear in matching artifacts only |
|
||||
| No schema selection | Explicit `schema:` field sets default workflow |
|
||||
|
||||
### What to Keep, What to Drop
|
||||
|
||||
When migrating, be selective. Ask yourself: "Does the AI need this for *every* planning request?"
|
||||
|
||||
**Good candidates for `context:`**
|
||||
- Tech stack (languages, frameworks, databases)
|
||||
- Key architectural patterns (monorepo, microservices, etc.)
|
||||
- Non-obvious constraints ("we can't use library X because...")
|
||||
- Critical conventions that often get ignored
|
||||
|
||||
**Move to `rules:` instead**
|
||||
- Artifact-specific formatting ("use Given/When/Then in specs")
|
||||
- Review criteria ("proposals must include rollback plans")
|
||||
- These only appear for the matching artifact, keeping other requests lighter
|
||||
|
||||
**Leave out entirely**
|
||||
- General best practices the AI already knows
|
||||
- Verbose explanations that could be summarized
|
||||
- Historical context that doesn't affect current work
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. **Create config.yaml** (if not already created by init):
|
||||
```yaml
|
||||
schema: spec-driven
|
||||
```
|
||||
|
||||
2. **Add your context** (be concise—this goes into every request):
|
||||
```yaml
|
||||
context: |
|
||||
Your project background goes here.
|
||||
Focus on what the AI genuinely needs to know.
|
||||
```
|
||||
|
||||
3. **Add per-artifact rules** (optional):
|
||||
```yaml
|
||||
rules:
|
||||
proposal:
|
||||
- Your proposal-specific guidance
|
||||
specs:
|
||||
- Your spec-writing rules
|
||||
```
|
||||
|
||||
4. **Delete project.md** once you've moved everything useful.
|
||||
|
||||
**Don't overthink it.** Start with the essentials and iterate. If you notice the AI missing something important, add it. If context feels bloated, trim it. This is a living document.
|
||||
|
||||
### Need Help? Use This Prompt
|
||||
|
||||
If you're unsure how to distill your project.md, ask your AI assistant:
|
||||
|
||||
```
|
||||
I'm migrating from OpenSpec's old project.md to the new config.yaml format.
|
||||
|
||||
Here's my current project.md:
|
||||
[paste your project.md content]
|
||||
|
||||
Please help me create a config.yaml with:
|
||||
1. A concise `context:` section (this gets injected into every planning request, so keep it tight—focus on tech stack, key constraints, and conventions that often get ignored)
|
||||
2. `rules:` for specific artifacts if any content is artifact-specific (e.g., "use Given/When/Then" belongs in specs rules, not global context)
|
||||
|
||||
Leave out anything generic that AI models already know. Be ruthless about brevity.
|
||||
```
|
||||
|
||||
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 | Purpose |
|
||||
|---------|---------|
|
||||
| `/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 |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes at once |
|
||||
|
||||
### Command Mapping from Legacy
|
||||
|
||||
| Legacy | OPSX Equivalent |
|
||||
|--------|-----------------|
|
||||
| `/openspec:proposal` | `/opsx:new` then `/opsx:ff` |
|
||||
| `/openspec:apply` | `/opsx:apply` |
|
||||
| `/openspec:archive` | `/opsx:archive` |
|
||||
|
||||
### New Capabilities
|
||||
|
||||
**Granular artifact creation:**
|
||||
```
|
||||
/opsx:continue
|
||||
```
|
||||
Creates one artifact at a time based on dependencies. Use this when you want to review each step.
|
||||
|
||||
**Exploration mode:**
|
||||
```
|
||||
/opsx:explore
|
||||
```
|
||||
Think through ideas with a partner before committing to a change.
|
||||
|
||||
---
|
||||
|
||||
## Understanding the New Architecture
|
||||
|
||||
### From Phase-Locked to Fluid
|
||||
|
||||
The legacy workflow forced linear progression:
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ PLANNING │ ───► │ IMPLEMENTING │ ───► │ ARCHIVING │
|
||||
│ PHASE │ │ PHASE │ │ PHASE │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
|
||||
If you're in implementation and realize the design is wrong?
|
||||
Too bad. Phase gates don't let you go back easily.
|
||||
```
|
||||
|
||||
OPSX uses actions, not phases:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────┐
|
||||
│ ACTIONS (not phases) │
|
||||
│ │
|
||||
│ new ◄──► continue ◄──► apply ◄──► archive │
|
||||
│ │ │ │ │ │
|
||||
│ └──────────┴───────────┴───────────┘ │
|
||||
│ any order │
|
||||
└────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Dependency Graph
|
||||
|
||||
Artifacts form a directed graph. Dependencies are enablers, not gates:
|
||||
|
||||
```
|
||||
proposal
|
||||
(root node)
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
specs design
|
||||
(requires: (requires:
|
||||
proposal) proposal)
|
||||
│ │
|
||||
└─────────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
tasks
|
||||
(requires:
|
||||
specs, design)
|
||||
```
|
||||
|
||||
When you run `/opsx:continue`, it checks what's ready and offers the next artifact. You can also create multiple ready artifacts in any order.
|
||||
|
||||
### Skills vs Commands
|
||||
|
||||
The legacy system used tool-specific command files:
|
||||
|
||||
```
|
||||
.claude/commands/openspec/
|
||||
├── proposal.md
|
||||
├── apply.md
|
||||
└── archive.md
|
||||
```
|
||||
|
||||
OPSX uses the emerging **skills** standard:
|
||||
|
||||
```
|
||||
.claude/skills/
|
||||
├── openspec-explore/SKILL.md
|
||||
├── openspec-new-change/SKILL.md
|
||||
├── openspec-continue-change/SKILL.md
|
||||
├── openspec-apply-change/SKILL.md
|
||||
└── ...
|
||||
```
|
||||
|
||||
Skills are recognized across multiple AI coding tools and provide richer metadata.
|
||||
|
||||
---
|
||||
|
||||
## Continuing Existing Changes
|
||||
|
||||
Your in-progress changes work seamlessly with OPSX commands.
|
||||
|
||||
**Have an active change from the legacy workflow?**
|
||||
|
||||
```
|
||||
/opsx:apply add-my-feature
|
||||
```
|
||||
|
||||
OPSX reads the existing artifacts and continues from where you left off.
|
||||
|
||||
**Want to add more artifacts to an existing change?**
|
||||
|
||||
```
|
||||
/opsx:continue add-my-feature
|
||||
```
|
||||
|
||||
Shows what's ready to create based on what already exists.
|
||||
|
||||
**Need to see status?**
|
||||
|
||||
```bash
|
||||
openspec status --change add-my-feature
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The New Config System
|
||||
|
||||
### config.yaml Structure
|
||||
|
||||
```yaml
|
||||
# Required: Default schema for new changes
|
||||
schema: spec-driven
|
||||
|
||||
# Optional: Project context (max 50KB)
|
||||
# Injected into ALL artifact instructions
|
||||
context: |
|
||||
Your project background, tech stack,
|
||||
conventions, and constraints.
|
||||
|
||||
# Optional: Per-artifact rules
|
||||
# Only injected into matching artifacts
|
||||
rules:
|
||||
proposal:
|
||||
- Include rollback plan
|
||||
specs:
|
||||
- Use Given/When/Then format
|
||||
design:
|
||||
- Document fallback strategies
|
||||
tasks:
|
||||
- Break into 2-hour maximum chunks
|
||||
```
|
||||
|
||||
### Schema Resolution
|
||||
|
||||
When determining which schema to use, OPSX checks in order:
|
||||
|
||||
1. **CLI flag**: `--schema <name>` (highest priority)
|
||||
2. **Change metadata**: `.openspec.yaml` in the change directory
|
||||
3. **Project config**: `openspec/config.yaml`
|
||||
4. **Default**: `spec-driven`
|
||||
|
||||
### Available Schemas
|
||||
|
||||
| Schema | Artifacts | Best For |
|
||||
|--------|-----------|----------|
|
||||
| `spec-driven` | proposal → specs → design → tasks | Most projects |
|
||||
|
||||
List all available schemas:
|
||||
|
||||
```bash
|
||||
openspec workflow schemas
|
||||
```
|
||||
|
||||
### Custom Schemas
|
||||
|
||||
Create your own workflow:
|
||||
|
||||
```bash
|
||||
openspec schema init my-workflow
|
||||
```
|
||||
|
||||
Or fork an existing one:
|
||||
|
||||
```bash
|
||||
openspec schema fork spec-driven my-workflow
|
||||
```
|
||||
|
||||
See [Customization](customization.md) for details.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Legacy files detected in non-interactive mode"
|
||||
|
||||
You're running in a CI or non-interactive environment. Use:
|
||||
|
||||
```bash
|
||||
openspec init --force
|
||||
```
|
||||
|
||||
### Commands not appearing after migration
|
||||
|
||||
Restart your IDE. Skills are detected at startup.
|
||||
|
||||
### "Unknown artifact ID in rules"
|
||||
|
||||
Check that your `rules:` keys match your schema's artifact IDs:
|
||||
|
||||
- **spec-driven**: `proposal`, `specs`, `design`, `tasks`
|
||||
|
||||
Run this to see valid artifact IDs:
|
||||
|
||||
```bash
|
||||
openspec workflow schemas --json
|
||||
```
|
||||
|
||||
### Config not being applied
|
||||
|
||||
1. Ensure the file is at `openspec/config.yaml` (not `.yml`)
|
||||
2. Validate YAML syntax
|
||||
3. Config changes take effect immediately—no restart needed
|
||||
|
||||
### project.md not migrated
|
||||
|
||||
The system intentionally preserves `project.md` because it may contain your custom content. Review it manually, move useful parts to `config.yaml`, then delete it.
|
||||
|
||||
### Want to see what would be cleaned up?
|
||||
|
||||
Run init and decline the cleanup prompt—you'll see the full detection summary without any changes being made.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Files After Migration
|
||||
|
||||
```
|
||||
project/
|
||||
├── openspec/
|
||||
│ ├── specs/ # Unchanged
|
||||
│ ├── changes/ # Unchanged
|
||||
│ │ └── archive/ # Unchanged
|
||||
│ └── config.yaml # NEW: Project configuration
|
||||
├── .claude/
|
||||
│ └── skills/ # NEW: OPSX skills
|
||||
│ ├── openspec-explore/
|
||||
│ ├── openspec-new-change/
|
||||
│ └── ...
|
||||
├── CLAUDE.md # OpenSpec markers removed, your content preserved
|
||||
└── AGENTS.md # OpenSpec markers removed, your content preserved
|
||||
```
|
||||
|
||||
### What's Gone
|
||||
|
||||
- `.claude/commands/openspec/` — replaced by `.claude/skills/`
|
||||
- `openspec/AGENTS.md` — obsolete
|
||||
- `openspec/project.md` — migrate to `config.yaml`, then delete
|
||||
- OpenSpec marker blocks in `CLAUDE.md`, `AGENTS.md`, etc.
|
||||
|
||||
### Command Cheatsheet
|
||||
|
||||
```
|
||||
/opsx:new Start a change
|
||||
/opsx:continue Create next artifact
|
||||
/opsx:ff Create all planning artifacts
|
||||
/opsx:apply Implement tasks
|
||||
/opsx:archive Finish and archive
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Discord**: [discord.gg/YctCnvvshC](https://discord.gg/YctCnvvshC)
|
||||
- **GitHub Issues**: [github.com/Fission-AI/OpenSpec/issues](https://github.com/Fission-AI/OpenSpec/issues)
|
||||
- **Documentation**: [docs/opsx.md](opsx.md) for the full OPSX reference
|
||||
@@ -0,0 +1,115 @@
|
||||
# Multi-Language Guide
|
||||
|
||||
Configure OpenSpec to generate artifacts in languages other than English.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
Add a language instruction to your `openspec/config.yaml`:
|
||||
|
||||
```yaml
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Language: Portuguese (pt-BR)
|
||||
All artifacts must be written in Brazilian Portuguese.
|
||||
|
||||
# Your other project context below...
|
||||
Tech stack: TypeScript, React, Node.js
|
||||
```
|
||||
|
||||
That's it. All generated artifacts will now be in Portuguese.
|
||||
|
||||
## Language Examples
|
||||
|
||||
### Portuguese (Brazil)
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
Language: Portuguese (pt-BR)
|
||||
All artifacts must be written in Brazilian Portuguese.
|
||||
```
|
||||
|
||||
### Spanish
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
Idioma: Español
|
||||
Todos los artefactos deben escribirse en español.
|
||||
```
|
||||
|
||||
### Chinese (Simplified)
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
语言:中文(简体)
|
||||
所有产出物必须用简体中文撰写。
|
||||
```
|
||||
|
||||
### Japanese
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
言語:日本語
|
||||
すべての成果物は日本語で作成してください。
|
||||
```
|
||||
|
||||
### French
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
Langue : Français
|
||||
Tous les artefacts doivent être rédigés en français.
|
||||
```
|
||||
|
||||
### German
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
Sprache: Deutsch
|
||||
Alle Artefakte müssen auf Deutsch verfasst werden.
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
### Handle Technical Terms
|
||||
|
||||
Decide how to handle technical terminology:
|
||||
|
||||
```yaml
|
||||
context: |
|
||||
Language: Japanese
|
||||
Write in Japanese, but:
|
||||
- Keep technical terms like "API", "REST", "GraphQL" in English
|
||||
- Code examples and file paths remain in English
|
||||
```
|
||||
|
||||
### Combine with Other Context
|
||||
|
||||
Language settings work alongside your other project context:
|
||||
|
||||
```yaml
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Language: Portuguese (pt-BR)
|
||||
All artifacts must be written in Brazilian Portuguese.
|
||||
|
||||
Tech stack: TypeScript, React 18, Node.js 20
|
||||
Database: PostgreSQL with Prisma ORM
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
To verify your language config is working:
|
||||
|
||||
```bash
|
||||
# Check the instructions - should show your language context
|
||||
openspec instructions proposal --change my-change
|
||||
|
||||
# Output will include your language context
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Customization Guide](./customization.md) - Project configuration options
|
||||
- [Workflows Guide](./workflows.md) - Full workflow documentation
|
||||
@@ -1,8 +1,6 @@
|
||||
# Experimental Workflow (OPSX)
|
||||
# OPSX Workflow
|
||||
|
||||
> **Status:** Experimental. Things might break. Feedback welcome on [Discord](https://discord.gg/BYjPaKbqMt).
|
||||
>
|
||||
> **Compatibility:** Claude Code only (for now)
|
||||
> **Compatibility:** Claude Code only (for now). Feedback welcome on [Discord](https://discord.gg/YctCnvvshC).
|
||||
|
||||
## What Is It?
|
||||
|
||||
@@ -51,19 +49,9 @@ You're "in planning phase", then "in implementation phase", then "done". But rea
|
||||
**OPSX approach:**
|
||||
- **Actions, not phases** — create, implement, update, archive — do any of them anytime
|
||||
- **Dependencies are enablers** — they show what's possible, not what's required next
|
||||
- **Update as you learn** — halfway through implementation? Go back and fix the design. That's normal.
|
||||
|
||||
```
|
||||
You can always go back:
|
||||
|
||||
┌────────────────────────────────────┐
|
||||
│ │
|
||||
▼ │
|
||||
proposal ──→ specs ──→ design ──→ tasks ──→ implement
|
||||
▲ ▲ ▲ │
|
||||
│ │ │ │
|
||||
└───────────┴──────────┴───────────────┘
|
||||
update as you learn
|
||||
```
|
||||
|
||||
## Setup
|
||||
@@ -73,7 +61,7 @@ You can always go back:
|
||||
openspec init
|
||||
|
||||
# 2. Generate the experimental skills
|
||||
openspec artifact-experimental-setup
|
||||
openspec experimental
|
||||
```
|
||||
|
||||
This creates skills in `.claude/skills/` that Claude Code auto-detects.
|
||||
@@ -86,7 +74,7 @@ Project config lets you set defaults and inject project-specific context into al
|
||||
|
||||
### Creating Config
|
||||
|
||||
Config is created during `artifact-experimental-setup`, or manually:
|
||||
Config is created during `experimental`, or manually:
|
||||
|
||||
```yaml
|
||||
# openspec/config.yaml
|
||||
@@ -112,14 +100,14 @@ rules:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `schema` | string | Default schema for new changes (e.g., `spec-driven`, `tdd`) |
|
||||
| `schema` | string | Default schema for new changes (e.g., `spec-driven`) |
|
||||
| `context` | string | Project context injected into all artifact instructions |
|
||||
| `rules` | object | Per-artifact rules, keyed by artifact ID |
|
||||
|
||||
### How It Works
|
||||
|
||||
**Schema precedence** (highest to lowest):
|
||||
1. CLI flag (`--schema tdd`)
|
||||
1. CLI flag (`--schema <name>`)
|
||||
2. Change metadata (`.openspec.yaml` in change directory)
|
||||
3. Project config (`openspec/config.yaml`)
|
||||
4. Default (`spec-driven`)
|
||||
@@ -142,12 +130,6 @@ rules:
|
||||
- `design` — Technical design
|
||||
- `tasks` — Implementation tasks
|
||||
|
||||
**tdd**:
|
||||
- `spec` — Feature specification
|
||||
- `tests` — Test file
|
||||
- `implementation` — Implementation code
|
||||
- `docs` — Documentation
|
||||
|
||||
### Config Validation
|
||||
|
||||
- Unknown artifact IDs in `rules` generate warnings
|
||||
@@ -179,7 +161,7 @@ rules:
|
||||
| `/opsx:continue` | Create the next artifact (based on what's ready) |
|
||||
| `/opsx:ff` | Fast-forward — create all planning artifacts at once |
|
||||
| `/opsx:apply` | Implement tasks, updating artifacts as needed |
|
||||
| `/opsx:sync` | Sync delta specs to main specs |
|
||||
| `/opsx:sync` | Sync delta specs to main (optional—archive prompts if needed) |
|
||||
| `/opsx:archive` | Archive when done |
|
||||
|
||||
## Usage
|
||||
@@ -211,17 +193,16 @@ Creates all planning artifacts at once. Use when you have a clear picture of wha
|
||||
```
|
||||
/opsx:apply
|
||||
```
|
||||
Works through tasks, checking them off as you go. **Key difference:** if you discover issues during implementation, you can update your specs, design, or tasks — then continue. No phase gates. 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.
|
||||
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.
|
||||
|
||||
### Finish up
|
||||
```
|
||||
/opsx:sync # Update main specs with your delta specs
|
||||
/opsx:archive # Move to archive when done
|
||||
/opsx:archive # Move to archive when done (prompts to sync specs if needed)
|
||||
```
|
||||
|
||||
## When to Update vs. Start Fresh
|
||||
|
||||
OPSX lets you update artifacts anytime. But when does "update as you learn" become "this is different work"?
|
||||
You can always edit your proposal or specs before implementation. But when does refining become "this is different work"?
|
||||
|
||||
### What a Proposal Captures
|
||||
|
||||
@@ -351,9 +332,9 @@ This section explains how OPSX works under the hood and how it compares to the s
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ ACTIONS (not phases) │ │
|
||||
│ │ │ │
|
||||
│ │ new ◄──► continue ◄──► apply ◄──► sync │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ └──────────┴───────────┴──────────┘ │ │
|
||||
│ │ new ◄──► continue ◄──► apply ◄──► archive │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ └──────────┴───────────┴───────────┘ │ │
|
||||
│ │ any order │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
@@ -476,7 +457,7 @@ Artifacts form a directed acyclic graph (DAG). Dependencies are **enablers**, no
|
||||
│ • Create proposal.md │
|
||||
│ • Create tasks.md │
|
||||
│ • Create design.md │
|
||||
│ • Create specs/*.md │
|
||||
│ • Create specs/<capability>/spec.md │
|
||||
│ │
|
||||
│ No awareness of what exists or │
|
||||
│ dependencies between artifacts │
|
||||
@@ -631,7 +612,6 @@ artifacts:
|
||||
Schemas define what artifacts exist and their dependencies. Currently available:
|
||||
|
||||
- **spec-driven** (default): proposal → specs → design → tasks
|
||||
- **tdd**: tests → implementation → docs
|
||||
|
||||
```bash
|
||||
# List available schemas
|
||||
@@ -662,4 +642,4 @@ openspec schema validate my-workflow
|
||||
|
||||
This is rough. That's intentional — we're learning what works.
|
||||
|
||||
Found a bug? Have ideas? Join us on [Discord](https://discord.gg/BYjPaKbqMt) or open an issue on [GitHub](https://github.com/Fission-AI/openspec/issues).
|
||||
Found a bug? Have ideas? Join us on [Discord](https://discord.gg/YctCnvvshC) or open an issue on [GitHub](https://github.com/Fission-AI/openspec/issues).
|
||||
@@ -1,205 +0,0 @@
|
||||
# Project Config Demo Guide
|
||||
|
||||
A quick-reference guide for demonstrating the `openspec/config.yaml` feature.
|
||||
|
||||
## Summary: What Project Config Does
|
||||
|
||||
The feature adds `openspec/config.yaml` as a lightweight customization layer that lets teams:
|
||||
|
||||
- **Set a default schema** - New changes automatically use this schema instead of having to specify `--schema` every time
|
||||
- **Inject project context** - Shared context (tech stack, conventions) shown to AI when creating any artifact
|
||||
- **Add per-artifact rules** - Custom rules that only apply to specific artifacts (e.g., proposal, specs)
|
||||
|
||||
## Demo Walkthrough
|
||||
|
||||
### Demo 1: Interactive Setup (Recommended Entry Point)
|
||||
|
||||
The easiest way to demo is through the experimental setup command:
|
||||
|
||||
```bash
|
||||
openspec artifact-experimental-setup
|
||||
```
|
||||
|
||||
After creating skills/commands, it will prompt:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Project Configuration (Optional)
|
||||
|
||||
Configure project defaults for OpenSpec workflows.
|
||||
|
||||
? Create openspec/config.yaml? (Y/n)
|
||||
```
|
||||
|
||||
Walk through:
|
||||
|
||||
1. **Select schema** - Shows available schemas with their artifact flows
|
||||
2. **Add context** - Opens editor for multi-line project context (tech stack, conventions)
|
||||
3. **Add rules** - Checkbox to select artifacts, then line-by-line rule entry
|
||||
|
||||
This creates `openspec/config.yaml` with the user's choices.
|
||||
|
||||
### Demo 2: Manual Config Creation
|
||||
|
||||
Show that users can create the config directly:
|
||||
|
||||
```bash
|
||||
cat > openspec/config.yaml << 'EOF'
|
||||
schema: spec-driven
|
||||
|
||||
context: |
|
||||
Tech stack: TypeScript, React, Node.js, PostgreSQL
|
||||
API style: RESTful, documented in docs/api.md
|
||||
Testing: Jest + React Testing Library
|
||||
We value backwards compatibility for all public APIs
|
||||
|
||||
rules:
|
||||
proposal:
|
||||
- Include rollback plan
|
||||
- Identify affected teams and notify in #platform-changes
|
||||
specs:
|
||||
- Use Given/When/Then format
|
||||
- Reference existing patterns before inventing new ones
|
||||
EOF
|
||||
```
|
||||
|
||||
### Demo 3: Effect on New Changes
|
||||
|
||||
Show that creating a new change now uses the default schema:
|
||||
|
||||
```bash
|
||||
# Before config: had to specify schema
|
||||
openspec new change my-feature --schema spec-driven
|
||||
|
||||
# After config: schema is automatic
|
||||
openspec new change my-feature
|
||||
# Automatically uses spec-driven from config
|
||||
```
|
||||
|
||||
### Demo 4: Context and Rules Injection
|
||||
|
||||
The key demo moment - show how instructions are enriched:
|
||||
|
||||
```bash
|
||||
# Get instructions for an artifact
|
||||
openspec instructions proposal --change my-feature
|
||||
```
|
||||
|
||||
Output shows the XML structure:
|
||||
|
||||
```xml
|
||||
<context>
|
||||
Tech stack: TypeScript, React, Node.js, PostgreSQL
|
||||
API style: RESTful, documented in docs/api.md
|
||||
...
|
||||
</context>
|
||||
|
||||
<rules>
|
||||
- Include rollback plan
|
||||
- Identify affected teams and notify in #platform-changes
|
||||
</rules>
|
||||
|
||||
<template>
|
||||
[Schema's built-in proposal template]
|
||||
</template>
|
||||
```
|
||||
|
||||
Key points to highlight:
|
||||
|
||||
- **Context** appears in ALL artifacts (proposal, specs, design, tasks)
|
||||
- **Rules** ONLY appear for the matching artifact (proposal rules only in proposal instructions)
|
||||
|
||||
### Demo 5: Precedence Override
|
||||
|
||||
Show the schema resolution order:
|
||||
|
||||
```bash
|
||||
# Config sets schema: spec-driven
|
||||
|
||||
# 1. CLI flag wins
|
||||
openspec new change feature-a --schema tdd # Uses tdd
|
||||
|
||||
# 2. Change metadata wins over config
|
||||
# (if .openspec.yaml in change directory specifies schema)
|
||||
|
||||
# 3. Config is used as default
|
||||
openspec new change feature-b # Uses spec-driven from config
|
||||
|
||||
# 4. Hardcoded default (no config)
|
||||
# Would fall back to spec-driven anyway
|
||||
```
|
||||
|
||||
### Demo 6: Validation and Error Handling
|
||||
|
||||
Show graceful error handling:
|
||||
|
||||
```bash
|
||||
# Create config with typo
|
||||
echo "schema: spec-drivne" > openspec/config.yaml
|
||||
|
||||
# Try to use it - shows fuzzy matching suggestions
|
||||
openspec new change test
|
||||
# Schema 'spec-drivne' not found
|
||||
# Did you mean: spec-driven (built-in)
|
||||
```
|
||||
|
||||
```bash
|
||||
# Unknown artifact ID in rules - warns but doesn't halt
|
||||
cat > openspec/config.yaml << 'EOF'
|
||||
schema: spec-driven
|
||||
rules:
|
||||
testplan: # Schema doesn't have this
|
||||
- Some rule
|
||||
EOF
|
||||
|
||||
openspec instructions proposal --change test
|
||||
# ⚠️ Unknown artifact ID in rules: "testplan". Valid IDs for schema "spec-driven": ...
|
||||
# (continues working)
|
||||
```
|
||||
|
||||
## Quick Demo Script
|
||||
|
||||
Here's a quick all-in-one demo:
|
||||
|
||||
```bash
|
||||
# 1. Show there's no config initially
|
||||
cat openspec/config.yaml 2>/dev/null || echo "No config exists"
|
||||
|
||||
# 2. Create a simple config
|
||||
cat > openspec/config.yaml << 'EOF'
|
||||
schema: spec-driven
|
||||
context: |
|
||||
This is a demo project using React and TypeScript.
|
||||
We follow semantic versioning.
|
||||
rules:
|
||||
proposal:
|
||||
- Include migration steps if breaking change
|
||||
EOF
|
||||
|
||||
# 3. Show the config
|
||||
cat openspec/config.yaml
|
||||
|
||||
# 4. Create a change (uses default schema from config)
|
||||
openspec new change demo-feature
|
||||
|
||||
# 5. Show instructions with injected context/rules
|
||||
openspec instructions proposal --change demo-feature | head -30
|
||||
|
||||
# 6. Show that specs don't have proposal rules
|
||||
openspec instructions specs --change demo-feature | head -30
|
||||
```
|
||||
|
||||
## What to Emphasize in Demo
|
||||
|
||||
- **Low friction** - Teams can customize without forking schemas
|
||||
- **Shared context** - Everyone on the team gets the same project knowledge
|
||||
- **Per-artifact rules** - Targeted guidance where it matters
|
||||
- **Graceful failures** - Typos warn, don't break workflow
|
||||
- **Team sharing** - Just commit `openspec/config.yaml` and everyone benefits
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Experimental Workflow Guide](./experimental-workflow.md) - Full user guide with config section
|
||||
- [Project Config Proposal](../openspec/changes/project-config/proposal.md) - Original design proposal
|
||||
- [Project Config Design](../openspec/changes/project-config/design.md) - Technical implementation details
|
||||
@@ -1,211 +0,0 @@
|
||||
# Schema Customization
|
||||
|
||||
This document describes how users can customize OpenSpec schemas and templates, the current manual process, and the gap that needs to be addressed.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
OpenSpec uses a 2-level schema resolution system following the XDG Base Directory Specification:
|
||||
|
||||
1. **User override**: `${XDG_DATA_HOME}/openspec/schemas/<name>/`
|
||||
2. **Package built-in**: `<npm-package>/schemas/<name>/`
|
||||
|
||||
When a schema is requested (e.g., `spec-driven`), the resolver checks the user directory first. If found, that entire schema directory is used. Otherwise, it falls back to the package's built-in schema.
|
||||
|
||||
---
|
||||
|
||||
## Current Manual Process
|
||||
|
||||
To override the default `spec-driven` schema, a user must:
|
||||
|
||||
### 1. Determine the correct directory path
|
||||
|
||||
| Platform | Path |
|
||||
|----------|------|
|
||||
| macOS/Linux | `~/.local/share/openspec/schemas/` |
|
||||
| Windows | `%LOCALAPPDATA%\openspec\schemas\` |
|
||||
| All (if set) | `$XDG_DATA_HOME/openspec/schemas/` |
|
||||
|
||||
### 2. Create the directory structure
|
||||
|
||||
```bash
|
||||
# macOS/Linux example
|
||||
mkdir -p ~/.local/share/openspec/schemas/spec-driven/templates
|
||||
```
|
||||
|
||||
### 3. Find and copy the default schema files
|
||||
|
||||
The user must locate the installed npm package to copy the defaults:
|
||||
|
||||
```bash
|
||||
# Find the package location (varies by install method)
|
||||
npm list -g openspec --parseable
|
||||
# or
|
||||
which openspec && readlink -f $(which openspec)
|
||||
|
||||
# Copy files from the package's schemas/ directory
|
||||
cp <package-path>/schemas/spec-driven/schema.yaml ~/.local/share/openspec/schemas/spec-driven/
|
||||
cp <package-path>/schemas/spec-driven/templates/*.md ~/.local/share/openspec/schemas/spec-driven/templates/
|
||||
```
|
||||
|
||||
### 4. Modify the copied files
|
||||
|
||||
Edit `schema.yaml` to change the workflow structure:
|
||||
|
||||
```yaml
|
||||
name: spec-driven
|
||||
version: 1
|
||||
description: My custom workflow
|
||||
artifacts:
|
||||
- id: proposal
|
||||
generates: proposal.md
|
||||
description: Initial proposal
|
||||
template: proposal.md
|
||||
requires: []
|
||||
# Add, remove, or modify artifacts...
|
||||
```
|
||||
|
||||
Edit templates in `templates/` to customize the content guidance.
|
||||
|
||||
### 5. Verify the override is active
|
||||
|
||||
Currently there's no command to verify which schema is being used. Users must trust that the file exists in the right location.
|
||||
|
||||
---
|
||||
|
||||
## Gap Analysis
|
||||
|
||||
The current process has several friction points:
|
||||
|
||||
| Issue | Impact |
|
||||
|-------|--------|
|
||||
| **Path discovery** | Users must know XDG conventions and platform-specific paths |
|
||||
| **Package location** | Finding the npm package path varies by install method (global, local, pnpm, yarn, volta, etc.) |
|
||||
| **No scaffolding** | Users must manually create directories and copy files |
|
||||
| **No verification** | No way to confirm which schema is actually being resolved |
|
||||
| **No diffing** | When upgrading openspec, users can't see what changed in built-in templates |
|
||||
| **Full copy required** | Must copy entire schema even to change one template |
|
||||
|
||||
### User Stories Not Currently Supported
|
||||
|
||||
1. *"I want to add a `research` artifact before `proposal`"* — requires manual copy and edit
|
||||
2. *"I want to customize just the proposal template"* — must copy entire schema
|
||||
3. *"I want to see what the default schema looks like"* — must find package path
|
||||
4. *"I want to revert to defaults"* — must delete files and hope paths are correct
|
||||
5. *"I upgraded openspec, did the templates change?"* — no way to diff
|
||||
|
||||
---
|
||||
|
||||
## Proposed Solution: Schema Configurator
|
||||
|
||||
A CLI command (or set of commands) that handles path resolution and file operations for users.
|
||||
|
||||
### Option A: Single `openspec schema` command
|
||||
|
||||
```bash
|
||||
# List available schemas (built-in and user overrides)
|
||||
openspec schema list
|
||||
|
||||
# Show where a schema resolves from
|
||||
openspec schema which spec-driven
|
||||
# Output: /Users/me/.local/share/openspec/schemas/spec-driven/ (user override)
|
||||
# Output: /usr/local/lib/node_modules/openspec/schemas/spec-driven/ (built-in)
|
||||
|
||||
# Copy a built-in schema to user directory for customization
|
||||
openspec schema copy spec-driven
|
||||
# Creates ~/.local/share/openspec/schemas/spec-driven/ with all files
|
||||
|
||||
# Show diff between user override and built-in
|
||||
openspec schema diff spec-driven
|
||||
|
||||
# Remove user override (revert to built-in)
|
||||
openspec schema reset spec-driven
|
||||
|
||||
# Validate a schema
|
||||
openspec schema validate spec-driven
|
||||
```
|
||||
|
||||
### Option B: Dedicated `openspec customize` command
|
||||
|
||||
```bash
|
||||
# Interactive schema customization
|
||||
openspec customize
|
||||
# Prompts: Which schema? What do you want to change? etc.
|
||||
|
||||
# Copy and open for editing
|
||||
openspec customize spec-driven
|
||||
# Copies to user dir, prints path, optionally opens in $EDITOR
|
||||
```
|
||||
|
||||
### Option C: Init-time schema selection
|
||||
|
||||
```bash
|
||||
# During project init, offer schema customization
|
||||
openspec init
|
||||
# ? Select a workflow schema:
|
||||
# > spec-driven (default)
|
||||
# tdd
|
||||
# minimal
|
||||
# custom (copy and edit)
|
||||
```
|
||||
|
||||
### Recommended Approach
|
||||
|
||||
**Option A** provides the most flexibility and follows Unix conventions (subcommands for discrete operations). Key commands in priority order:
|
||||
|
||||
1. `openspec schema list` — see what's available
|
||||
2. `openspec schema which <name>` — debug resolution
|
||||
3. `openspec schema copy <name>` — scaffold customization
|
||||
4. `openspec schema diff <name>` — compare with built-in
|
||||
5. `openspec schema reset <name>` — revert to defaults
|
||||
|
||||
---
|
||||
|
||||
## Implementation Considerations
|
||||
|
||||
### Path Resolution
|
||||
|
||||
The resolver already exists in `src/core/artifact-graph/resolver.ts`:
|
||||
|
||||
```typescript
|
||||
export function getPackageSchemasDir(): string { ... }
|
||||
export function getUserSchemasDir(): string { ... }
|
||||
export function getSchemaDir(name: string): string | null { ... }
|
||||
export function listSchemas(): string[] { ... }
|
||||
```
|
||||
|
||||
New commands would leverage these existing functions.
|
||||
|
||||
### File Operations
|
||||
|
||||
- Copy should preserve file permissions
|
||||
- Copy should not overwrite existing user files without `--force`
|
||||
- Reset should prompt for confirmation
|
||||
|
||||
### Template-Only Overrides
|
||||
|
||||
A future enhancement could support overriding individual templates without copying the entire schema. This would require changes to the resolution logic:
|
||||
|
||||
```
|
||||
Current: schema dir (user) OR schema dir (built-in)
|
||||
Future: schema.yaml from user OR built-in
|
||||
+ each template from user OR built-in (independent fallback)
|
||||
```
|
||||
|
||||
This adds complexity but enables the "I just want to change one template" use case.
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Schema Workflow Gaps](./schema-workflow-gaps.md) — End-to-end workflow analysis and phased implementation plan
|
||||
|
||||
## Related Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/core/artifact-graph/resolver.ts` | Schema resolution logic |
|
||||
| `src/core/artifact-graph/instruction-loader.ts` | Template loading |
|
||||
| `src/core/global-config.ts` | XDG path helpers |
|
||||
| `schemas/spec-driven/` | Default schema and templates |
|
||||
@@ -1,379 +0,0 @@
|
||||
# Schema Workflow: End-to-End Analysis
|
||||
|
||||
This document analyzes the complete user journey for working with schemas in OpenSpec, identifies gaps, and proposes a phased solution.
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
### What Exists
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| Schema resolution | 3-level: project → user → package (PR #522) |
|
||||
| Built-in schemas | `spec-driven`, `tdd` |
|
||||
| Artifact workflow commands | `status`, `next`, `instructions`, `templates` with `--schema` flag |
|
||||
| Change creation | `openspec new change <name>` — no schema binding |
|
||||
| Project-local schemas | ✅ Supported via `openspec/schemas/` (PR #522) |
|
||||
| Schema management CLI | ✅ `schema which`, `validate`, `fork`, `init` (PR #525) |
|
||||
|
||||
### What's Missing
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| Schema bound to change | Not stored — must pass `--schema` every time |
|
||||
| Project default schema | None — hardcoded to `spec-driven` |
|
||||
|
||||
---
|
||||
|
||||
## User Journey Analysis
|
||||
|
||||
### Scenario 1: Using a Non-Default Schema
|
||||
|
||||
**Goal:** User wants to use TDD workflow for a new feature.
|
||||
|
||||
**Today's experience:**
|
||||
```bash
|
||||
openspec new change add-auth
|
||||
# Creates directory, no schema info stored
|
||||
|
||||
openspec status --change add-auth
|
||||
# Shows spec-driven artifacts (WRONG - user wanted TDD)
|
||||
|
||||
# User realizes mistake...
|
||||
openspec status --change add-auth --schema tdd
|
||||
# Correct, but must remember --schema every time
|
||||
|
||||
# 6 months later...
|
||||
openspec status --change add-auth
|
||||
# Wrong again - nobody remembers this was TDD
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Schema is a runtime argument, not persisted
|
||||
- Easy to forget `--schema` and get wrong results
|
||||
- No record of intended schema for future reference
|
||||
|
||||
---
|
||||
|
||||
### Scenario 2: Customizing a Schema
|
||||
|
||||
**Goal:** User wants to add a "research" artifact before "proposal".
|
||||
|
||||
**Today's experience:**
|
||||
```bash
|
||||
# Step 1: Figure out where to put overrides
|
||||
# Must know XDG conventions:
|
||||
# macOS/Linux: ~/.local/share/openspec/schemas/
|
||||
# Windows: %LOCALAPPDATA%\openspec\schemas/
|
||||
|
||||
# Step 2: Create directory structure
|
||||
mkdir -p ~/.local/share/openspec/schemas/my-workflow/templates
|
||||
|
||||
# Step 3: Find the npm package to copy defaults
|
||||
npm list -g openspec --parseable
|
||||
# Output varies by package manager:
|
||||
# npm: /usr/local/lib/node_modules/openspec
|
||||
# pnpm: ~/.local/share/pnpm/global/5/node_modules/openspec
|
||||
# volta: ~/.volta/tools/image/packages/openspec/...
|
||||
# yarn: ~/.config/yarn/global/node_modules/openspec
|
||||
|
||||
# Step 4: Copy files
|
||||
cp -r <package-path>/schemas/spec-driven/* \
|
||||
~/.local/share/openspec/schemas/my-workflow/
|
||||
|
||||
# Step 5: Edit schema.yaml and templates
|
||||
# No way to verify override is active
|
||||
# No way to diff against original
|
||||
```
|
||||
|
||||
**Problems:**
|
||||
- Must know XDG path conventions
|
||||
- Finding npm package path varies by install method
|
||||
- No tooling to scaffold or verify
|
||||
- No diff capability when upgrading openspec
|
||||
|
||||
---
|
||||
|
||||
### Scenario 3: Team Sharing Custom Workflow
|
||||
|
||||
**Goal:** Team wants everyone to use the same custom schema.
|
||||
|
||||
**Today's options:**
|
||||
1. Everyone manually sets up XDG override — error-prone, drift risk
|
||||
2. Document setup in README — still manual, easy to miss
|
||||
3. Publish separate npm package — overkill for most teams
|
||||
4. Check schema into repo — **not supported** (no project-local resolution)
|
||||
|
||||
**Problems:**
|
||||
- No project-local schema resolution
|
||||
- Can't version control custom schemas with the codebase
|
||||
- No single source of truth for team workflow
|
||||
|
||||
---
|
||||
|
||||
## Gap Summary
|
||||
|
||||
| Gap | Impact | Status |
|
||||
|-----|--------|--------|
|
||||
| Schema not bound to change | Wrong results, forgotten context | ⏳ Pending (Phase 1) |
|
||||
| No project-local schemas | Can't share via repo | ✅ Fixed (PR #522) |
|
||||
| No schema management CLI | Manual path hunting | ✅ Fixed (PR #525) |
|
||||
| No project default schema | Must specify every time | ⏳ Pending (Phase 4) |
|
||||
| No init-time schema selection | Missed setup opportunity | ⏳ Pending (Phase 4) |
|
||||
|
||||
---
|
||||
|
||||
## Proposed Architecture
|
||||
|
||||
### New File Structure
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── config.yaml # Project config (NEW)
|
||||
├── schemas/ # Project-local schemas (NEW)
|
||||
│ └── my-workflow/
|
||||
│ ├── schema.yaml
|
||||
│ └── templates/
|
||||
│ ├── research.md
|
||||
│ ├── proposal.md
|
||||
│ └── ...
|
||||
└── changes/
|
||||
└── add-auth/
|
||||
├── change.yaml # Change metadata (NEW)
|
||||
├── proposal.md
|
||||
└── ...
|
||||
```
|
||||
|
||||
### config.yaml (Project Config)
|
||||
|
||||
```yaml
|
||||
# openspec/config.yaml
|
||||
defaultSchema: spec-driven
|
||||
```
|
||||
|
||||
Sets the project-wide default schema. Used when:
|
||||
- Creating new changes without `--schema`
|
||||
- Running commands on changes without `change.yaml`
|
||||
|
||||
### change.yaml (Change Metadata)
|
||||
|
||||
```yaml
|
||||
# openspec/changes/add-auth/change.yaml
|
||||
schema: tdd
|
||||
created: 2025-01-15T10:30:00Z
|
||||
description: Add user authentication system
|
||||
```
|
||||
|
||||
Binds a specific schema to a change. Created automatically by `openspec new change`.
|
||||
|
||||
### Schema Resolution Order
|
||||
|
||||
```
|
||||
1. ./openspec/schemas/<name>/ # Project-local
|
||||
2. ~/.local/share/openspec/schemas/<name>/ # User global (XDG)
|
||||
3. <npm-package>/schemas/<name>/ # Built-in
|
||||
```
|
||||
|
||||
Project-local takes priority, enabling version-controlled custom schemas.
|
||||
|
||||
### Schema Selection Order (Per Command)
|
||||
|
||||
```
|
||||
1. --schema CLI flag # Explicit override
|
||||
2. change.yaml in change directory # Change-specific binding
|
||||
3. openspec/config.yaml defaultSchema # Project default
|
||||
4. "spec-driven" # Hardcoded fallback
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ideal User Experience
|
||||
|
||||
### Creating a Change
|
||||
|
||||
```bash
|
||||
# Uses project default (from config.yaml, or spec-driven)
|
||||
openspec new change add-auth
|
||||
# Creates openspec/changes/add-auth/change.yaml:
|
||||
# schema: spec-driven
|
||||
# created: 2025-01-15T10:30:00Z
|
||||
|
||||
# Explicit schema for this change
|
||||
openspec new change add-auth --schema tdd
|
||||
# Creates change.yaml with schema: tdd
|
||||
```
|
||||
|
||||
### Working with Changes
|
||||
|
||||
```bash
|
||||
# Auto-reads schema from change.yaml — no --schema needed
|
||||
openspec status --change add-auth
|
||||
# Output: "Change: add-auth (schema: tdd)"
|
||||
# Shows which artifacts are ready/blocked/done
|
||||
|
||||
# Explicit override still works (with informational message)
|
||||
openspec status --change add-auth --schema spec-driven
|
||||
# "Note: change.yaml specifies 'tdd', using 'spec-driven' per --schema flag"
|
||||
```
|
||||
|
||||
### Customizing Schemas
|
||||
|
||||
```bash
|
||||
# See what's available
|
||||
openspec schema list
|
||||
# Built-in:
|
||||
# spec-driven proposal → specs → design → tasks
|
||||
# tdd spec → tests → implementation → docs
|
||||
# Project: (none)
|
||||
# User: (none)
|
||||
|
||||
# Copy to project for customization
|
||||
openspec schema copy spec-driven my-workflow
|
||||
# Created ./openspec/schemas/my-workflow/
|
||||
# Edit schema.yaml and templates/ to customize
|
||||
|
||||
# Copy to global (user-level override)
|
||||
openspec schema copy spec-driven --global
|
||||
# Created ~/.local/share/openspec/schemas/spec-driven/
|
||||
|
||||
# See where a schema resolves from
|
||||
openspec schema which spec-driven
|
||||
# ./openspec/schemas/spec-driven/ (project)
|
||||
# or: ~/.local/share/openspec/schemas/spec-driven/ (user)
|
||||
# or: /usr/local/lib/node_modules/openspec/schemas/spec-driven/ (built-in)
|
||||
|
||||
# Compare override with built-in
|
||||
openspec schema diff spec-driven
|
||||
# Shows diff between user/project version and package built-in
|
||||
|
||||
# Remove override, revert to built-in
|
||||
openspec schema reset spec-driven
|
||||
# Removes ./openspec/schemas/spec-driven/ (or --global for user dir)
|
||||
```
|
||||
|
||||
### Project Setup
|
||||
|
||||
```bash
|
||||
openspec init
|
||||
# ? Select default workflow schema:
|
||||
# > spec-driven (proposal → specs → design → tasks)
|
||||
# tdd (spec → tests → implementation → docs)
|
||||
# (custom schemas if detected)
|
||||
#
|
||||
# Writes to openspec/config.yaml:
|
||||
# defaultSchema: spec-driven
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Change Metadata (change.yaml)
|
||||
|
||||
**Priority:** High
|
||||
**Solves:** "Forgot --schema", lost context, wrong results
|
||||
|
||||
**Scope:**
|
||||
- Create `change.yaml` when running `openspec new change`
|
||||
- Store `schema`, `created` timestamp
|
||||
- Modify workflow commands to read schema from `change.yaml`
|
||||
- `--schema` flag overrides (with informational message)
|
||||
- Backwards compatible: missing `change.yaml` → use default
|
||||
|
||||
**change.yaml format:**
|
||||
```yaml
|
||||
schema: tdd
|
||||
created: 2025-01-15T10:30:00Z
|
||||
```
|
||||
|
||||
**Migration:**
|
||||
- Existing changes without `change.yaml` continue to work
|
||||
- Default to `spec-driven` (current behavior)
|
||||
- Optional: `openspec migrate` to add `change.yaml` to existing changes
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Project-Local Schemas
|
||||
|
||||
**Status:** ✅ Complete (PR #522)
|
||||
**Solves:** Team sharing, version control, no XDG knowledge needed
|
||||
|
||||
**Implemented:**
|
||||
- `./openspec/schemas/` added to resolution order (first priority)
|
||||
- `openspec schema fork <name> [new-name]` creates in project by default
|
||||
- Teams can commit `openspec/schemas/` to repo
|
||||
|
||||
**Resolution order:**
|
||||
```
|
||||
1. ./openspec/schemas/<name>/ # Project-local
|
||||
2. ~/.local/share/openspec/schemas/<name>/ # User global
|
||||
3. <npm-package>/schemas/<name>/ # Built-in
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Schema Management CLI
|
||||
|
||||
**Status:** ✅ Complete (PR #525)
|
||||
**Solves:** Path discovery, scaffolding, debugging
|
||||
|
||||
**Implemented Commands:**
|
||||
```bash
|
||||
openspec schema which [name] # Show resolution path, --all for all schemas
|
||||
openspec schema validate [name] # Validate schema structure and templates
|
||||
openspec schema fork <source> [name] # Copy existing schema for customization
|
||||
openspec schema init <name> # Create new project-local schema (interactive)
|
||||
```
|
||||
|
||||
**Not implemented (may add later):**
|
||||
- `schema diff` — Compare override with built-in
|
||||
- `schema reset` — Remove override, revert to built-in
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Project Config + Init Enhancement
|
||||
|
||||
**Priority:** Low
|
||||
**Solves:** Project-wide defaults, streamlined setup
|
||||
|
||||
**Scope:**
|
||||
- Add `openspec/config.yaml` with `defaultSchema` field
|
||||
- `openspec init` prompts for schema selection
|
||||
- Store selection in `config.yaml`
|
||||
- Commands use as fallback when no `change.yaml` exists
|
||||
|
||||
**config.yaml format:**
|
||||
```yaml
|
||||
defaultSchema: spec-driven
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backwards Compatibility
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| Existing change without `change.yaml` | Uses `--schema` flag or project default or `spec-driven` |
|
||||
| Existing project without `config.yaml` | Falls back to `spec-driven` |
|
||||
| `--schema` flag provided | Overrides `change.yaml` (with info message) |
|
||||
| No project-local schemas dir | Skipped in resolution, checks user/built-in |
|
||||
|
||||
All existing functionality continues to work. New features are additive.
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Schema Customization](./schema-customization.md) — Details on manual override process and CLI gaps
|
||||
- [Artifact POC](./artifact_poc.md) — Core artifact graph architecture
|
||||
|
||||
## Related Code
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/core/artifact-graph/resolver.ts` | Schema resolution logic |
|
||||
| `src/core/artifact-graph/instruction-loader.ts` | Template loading |
|
||||
| `src/core/global-config.ts` | XDG path helpers |
|
||||
| `src/commands/artifact-workflow.ts` | CLI commands |
|
||||
| `src/utils/change-utils.ts` | Change creation utilities |
|
||||
@@ -0,0 +1,84 @@
|
||||
# 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.
|
||||
|
||||
## How It Works
|
||||
|
||||
For each tool you select, OpenSpec installs:
|
||||
|
||||
1. **Skills** — Reusable instruction files that power the `/opsx:*` workflow commands
|
||||
2. **Commands** — Tool-specific slash command bindings
|
||||
|
||||
## 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/` |
|
||||
| OpenCode | `.opencode/skills/` | `.opencode/command/` |
|
||||
| Qoder | `.qoder/skills/` | `.qoder/commands/opsx/` |
|
||||
| Qwen Code | `.qwen/skills/` | `.qwen/commands/` |
|
||||
| RooCode | `.roo/skills/` | `.roo/commands/` |
|
||||
| Windsurf | `.windsurf/skills/` | `.windsurf/commands/opsx/` |
|
||||
|
||||
## Non-Interactive Setup
|
||||
|
||||
For CI/CD or scripted setup, use the `--tools` flag:
|
||||
|
||||
```bash
|
||||
# Configure specific tools
|
||||
openspec init --tools claude,cursor
|
||||
|
||||
# Configure all supported tools
|
||||
openspec init --tools all
|
||||
|
||||
# Skip tool configuration
|
||||
openspec init --tools none
|
||||
```
|
||||
|
||||
**Available tool IDs:** `amazon-q`, `antigravity`, `auggie`, `claude`, `cline`, `codebuddy`, `codex`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `kilocode`, `opencode`, `qoder`, `qwen`, `roocode`, `windsurf`
|
||||
|
||||
## What Gets Installed
|
||||
|
||||
For each tool, OpenSpec generates 10 skill files that power the OPSX workflow:
|
||||
|
||||
| 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 |
|
||||
|
||||
These skills are invoked via slash commands like `/opsx:new`, `/opsx:apply`, etc. See [Commands](commands.md) for the full list.
|
||||
|
||||
## Adding a New Tool
|
||||
|
||||
Want to add support for another AI coding assistant? Check out the [command adapter pattern](../CONTRIBUTING.md) or open an issue on GitHub.
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [CLI Reference](cli.md) — Terminal commands
|
||||
- [Commands](commands.md) — Slash commands and skills
|
||||
- [Getting Started](getting-started.md) — First-time setup
|
||||
@@ -0,0 +1,425 @@
|
||||
# Workflows
|
||||
|
||||
This guide covers common workflow patterns for OpenSpec and when to use each one. For basic setup, see [Getting Started](getting-started.md). For command reference, see [Commands](commands.md).
|
||||
|
||||
## Philosophy: Actions, Not Phases
|
||||
|
||||
Traditional workflows force you through phases: planning, then implementation, then done. But real work doesn't fit neatly into boxes.
|
||||
|
||||
OPSX takes a different approach:
|
||||
|
||||
```text
|
||||
Traditional (phase-locked):
|
||||
|
||||
PLANNING ────────► IMPLEMENTING ────────► DONE
|
||||
│ │
|
||||
│ "Can't go back" │
|
||||
└────────────────────┘
|
||||
|
||||
OPSX (fluid actions):
|
||||
|
||||
proposal ──► specs ──► design ──► tasks ──► implement
|
||||
```
|
||||
|
||||
**Key principles:**
|
||||
|
||||
- **Actions, not phases** - Commands are things you can do, not stages you're stuck in
|
||||
- **Dependencies are enablers** - They show what's possible, not what's required next
|
||||
|
||||
> **Customization:** OPSX workflows are driven by schemas that define artifact sequences. See [Customization](customization.md) for details on creating custom schemas.
|
||||
|
||||
## Workflow Patterns
|
||||
|
||||
### Quick Feature
|
||||
|
||||
When you know what you want to build and just need to execute:
|
||||
|
||||
```text
|
||||
/opsx:new ──► /opsx:ff ──► /opsx:apply ──► /opsx:verify ──► /opsx:archive
|
||||
```
|
||||
|
||||
**Example conversation:**
|
||||
|
||||
```text
|
||||
You: /opsx:new add-logout-button
|
||||
|
||||
AI: Created openspec/changes/add-logout-button/
|
||||
Ready to create: proposal
|
||||
|
||||
You: /opsx:ff
|
||||
|
||||
AI: Creating all planning artifacts...
|
||||
✓ proposal.md
|
||||
✓ specs/
|
||||
✓ design.md
|
||||
✓ tasks.md
|
||||
Ready for implementation!
|
||||
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Working through tasks...
|
||||
✓ Added logout button to header
|
||||
✓ Implemented session clearing
|
||||
✓ Added confirmation dialog
|
||||
All tasks complete!
|
||||
|
||||
You: /opsx:archive
|
||||
|
||||
AI: ✓ Merged specs
|
||||
✓ Archived change
|
||||
```
|
||||
|
||||
**Best for:** Small to medium features, bug fixes, straightforward changes.
|
||||
|
||||
### Exploratory
|
||||
|
||||
When requirements are unclear or you need to investigate first:
|
||||
|
||||
```text
|
||||
/opsx:explore ──► /opsx:new ──► /opsx:continue ──► ... ──► /opsx:apply
|
||||
```
|
||||
|
||||
**Example conversation:**
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: I want to improve page load performance but I'm not sure
|
||||
where the bottlenecks are.
|
||||
|
||||
AI: Let me help investigate...
|
||||
[Analyzes bundle sizes, identifies slow queries, reviews
|
||||
component render patterns]
|
||||
|
||||
I found three main bottlenecks:
|
||||
1. Large unoptimized images
|
||||
2. Synchronous data fetching in ProductList
|
||||
3. Re-renders from context changes
|
||||
|
||||
Which would you like to address first?
|
||||
|
||||
You: Let's tackle the data fetching issue.
|
||||
|
||||
You: /opsx:new optimize-product-list-fetching
|
||||
|
||||
AI: Created change. Ready to create: proposal
|
||||
|
||||
You: /opsx:continue
|
||||
|
||||
AI: Creating proposal based on our exploration...
|
||||
[Writes proposal incorporating investigation insights]
|
||||
|
||||
Ready to create: specs, design
|
||||
Which would you like next?
|
||||
|
||||
You: /opsx:continue
|
||||
|
||||
AI: Creating specs...
|
||||
```
|
||||
|
||||
**Best for:** Performance optimization, debugging, architectural decisions, unclear requirements.
|
||||
|
||||
### Parallel Changes
|
||||
|
||||
Work on multiple changes at once:
|
||||
|
||||
```text
|
||||
Change A: /opsx:new ──► /opsx:ff ──► /opsx:apply (in progress)
|
||||
│
|
||||
context switch
|
||||
│
|
||||
Change B: /opsx:new ──► /opsx:ff ──────► /opsx:apply
|
||||
```
|
||||
|
||||
**Example conversation:**
|
||||
|
||||
```text
|
||||
You: I'm working on add-dark-mode but got pulled into a bug fix
|
||||
|
||||
You: /opsx:new fix-login-redirect
|
||||
|
||||
AI: Created openspec/changes/fix-login-redirect/
|
||||
|
||||
You: /opsx:ff
|
||||
|
||||
AI: ✓ Created planning artifacts
|
||||
|
||||
You: /opsx:apply
|
||||
|
||||
AI: Working on fix-login-redirect...
|
||||
[Implements the fix]
|
||||
All tasks complete!
|
||||
|
||||
You: /opsx:archive
|
||||
|
||||
AI: ✓ Archived fix-login-redirect
|
||||
|
||||
You: Let's get back to dark mode
|
||||
|
||||
You: /opsx:apply add-dark-mode
|
||||
|
||||
AI: Resuming add-dark-mode...
|
||||
Picking up at task 2.3: Update Header...
|
||||
```
|
||||
|
||||
**Best for:** Parallel work streams, urgent interrupts, team collaboration.
|
||||
|
||||
When you have multiple completed changes, use `/opsx:bulk-archive`:
|
||||
|
||||
```text
|
||||
You: /opsx:bulk-archive
|
||||
|
||||
AI: Found 3 completed changes:
|
||||
- add-dark-mode (all tasks done)
|
||||
- fix-login-redirect (all tasks done)
|
||||
- update-footer (all tasks done)
|
||||
|
||||
Checking for spec conflicts...
|
||||
⚠ add-dark-mode and update-footer both touch specs/ui/
|
||||
|
||||
Inspecting codebase to resolve...
|
||||
Both changes are implemented. Will apply in chronological order.
|
||||
|
||||
Archive all 3 changes?
|
||||
|
||||
You: Yes
|
||||
|
||||
AI: ✓ Archived add-dark-mode
|
||||
✓ Archived fix-login-redirect
|
||||
✓ Archived update-footer
|
||||
Specs merged in order: add-dark-mode → update-footer
|
||||
```
|
||||
|
||||
Bulk archive detects when multiple changes touch the same specs and resolves conflicts by checking what's actually implemented.
|
||||
|
||||
### Completing a Change
|
||||
|
||||
The recommended completion flow:
|
||||
|
||||
```text
|
||||
/opsx:apply ──► /opsx:verify ──► /opsx:archive
|
||||
│ │
|
||||
validates prompts to sync
|
||||
implementation if needed
|
||||
```
|
||||
|
||||
#### Verify: Check Your Work
|
||||
|
||||
`/opsx:verify` validates implementation against your artifacts across three dimensions:
|
||||
|
||||
```text
|
||||
You: /opsx:verify
|
||||
|
||||
AI: Verifying add-auth...
|
||||
|
||||
COMPLETENESS
|
||||
✓ All 12 tasks in tasks.md are checked
|
||||
✓ All requirements in specs have corresponding code
|
||||
⚠ Scenario "Session timeout after inactivity" not tested
|
||||
|
||||
CORRECTNESS
|
||||
✓ Implementation matches spec intent
|
||||
✓ Edge cases from scenarios are handled
|
||||
✓ Error states match spec definitions
|
||||
|
||||
COHERENCE
|
||||
✓ Design decisions reflected in code structure
|
||||
✓ Naming conventions consistent with design.md
|
||||
⚠ Design mentions "event-driven" but implementation uses polling
|
||||
|
||||
SUMMARY
|
||||
─────────────────────────────
|
||||
Critical issues: 0
|
||||
Warnings: 2
|
||||
Ready to archive: Yes (with warnings)
|
||||
|
||||
Recommendations:
|
||||
1. Add test for session timeout scenario
|
||||
2. Consider refactoring to event-driven as designed, or update design.md
|
||||
```
|
||||
|
||||
**What verify checks:**
|
||||
|
||||
| Dimension | What it validates |
|
||||
|-----------|------------------|
|
||||
| Completeness | All tasks done, all requirements implemented, scenarios covered |
|
||||
| Correctness | Implementation matches spec intent, edge cases handled |
|
||||
| Coherence | Design decisions reflected in code, patterns consistent |
|
||||
|
||||
Verify won't block archive, but it surfaces issues you might want to address first.
|
||||
|
||||
#### Archive: Finalize the Change
|
||||
|
||||
`/opsx:archive` completes the change and moves it to the archive:
|
||||
|
||||
```text
|
||||
You: /opsx:archive
|
||||
|
||||
AI: Archiving add-auth...
|
||||
|
||||
Artifact status:
|
||||
✓ proposal.md exists
|
||||
✓ specs/ exists
|
||||
✓ design.md exists
|
||||
✓ tasks.md exists (12/12 tasks complete)
|
||||
|
||||
Delta specs: Not yet synced to main
|
||||
→ Would you like to sync now?
|
||||
|
||||
You: Yes
|
||||
|
||||
AI: ✓ Synced specs to openspec/specs/auth/spec.md
|
||||
✓ Moved to openspec/changes/archive/2025-01-24-add-auth/
|
||||
|
||||
Change archived successfully.
|
||||
```
|
||||
|
||||
Archive will prompt if specs aren't synced. It won't block on incomplete tasks, but it will warn you.
|
||||
|
||||
## When to Use What
|
||||
|
||||
### `/opsx:ff` vs `/opsx:continue`
|
||||
|
||||
| Situation | Use |
|
||||
|-----------|-----|
|
||||
| Clear requirements, ready to build | `/opsx:ff` |
|
||||
| Exploring, want to review each step | `/opsx:continue` |
|
||||
| Want to iterate on proposal before specs | `/opsx:continue` |
|
||||
| Time pressure, need to move fast | `/opsx:ff` |
|
||||
| Complex change, want control | `/opsx:continue` |
|
||||
|
||||
**Rule of thumb:** If you can describe the full scope upfront, use `/opsx:ff`. If you're figuring it out as you go, use `/opsx:continue`.
|
||||
|
||||
### When to Update vs Start Fresh
|
||||
|
||||
A common question: when is updating an existing change okay, and when should you start a new one?
|
||||
|
||||
**Update the existing change when:**
|
||||
|
||||
- Same intent, refined execution
|
||||
- Scope narrows (MVP first, rest later)
|
||||
- Learning-driven corrections (codebase isn't what you expected)
|
||||
- Design tweaks based on implementation discoveries
|
||||
|
||||
**Start a new change when:**
|
||||
|
||||
- Intent fundamentally changed
|
||||
- Scope exploded to different work entirely
|
||||
- Original change can be marked "done" standalone
|
||||
- Patches would confuse more than clarify
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────┐
|
||||
│ Is this the same work? │
|
||||
└──────────────┬──────────────────────┘
|
||||
│
|
||||
┌──────────────────┼──────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Same intent? >50% overlap? Can original
|
||||
Same problem? Same scope? be "done" without
|
||||
│ │ these changes?
|
||||
│ │ │
|
||||
┌────────┴────────┐ ┌──────┴──────┐ ┌───────┴───────┐
|
||||
│ │ │ │ │ │
|
||||
YES NO YES NO NO YES
|
||||
│ │ │ │ │ │
|
||||
▼ ▼ ▼ ▼ ▼ ▼
|
||||
UPDATE NEW UPDATE NEW UPDATE NEW
|
||||
```
|
||||
|
||||
**Example: "Add dark mode"**
|
||||
|
||||
- "Need to also support custom themes" → New change (scope exploded)
|
||||
- "System preference detection is harder than expected" → Update (same intent)
|
||||
- "Let's ship toggle first, add preferences later" → Update then archive, then new change
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Keep Changes Focused
|
||||
|
||||
One logical unit of work per change. If you're doing "add feature X and also refactor Y", consider two separate changes.
|
||||
|
||||
**Why it matters:**
|
||||
- Easier to review and understand
|
||||
- Cleaner archive history
|
||||
- Can ship independently
|
||||
- Simpler rollback if needed
|
||||
|
||||
### Use `/opsx:explore` for Unclear Requirements
|
||||
|
||||
Before committing to a change, explore the problem space:
|
||||
|
||||
```text
|
||||
You: /opsx:explore
|
||||
|
||||
AI: What would you like to explore?
|
||||
|
||||
You: How should we handle rate limiting for our API?
|
||||
|
||||
AI: Let me investigate your current setup and options...
|
||||
[Researches codebase, considers patterns]
|
||||
|
||||
I see three approaches we could take:
|
||||
1. Middleware-based (simple, per-endpoint)
|
||||
2. Redis-backed (scalable, shared state)
|
||||
3. Token bucket per user (fair, complex)
|
||||
|
||||
Your current stack suggests #1 or #2. What's your scale?
|
||||
```
|
||||
|
||||
Exploration clarifies thinking before you create artifacts.
|
||||
|
||||
### Verify Before Archiving
|
||||
|
||||
Use `/opsx:verify` to check implementation matches artifacts:
|
||||
|
||||
```text
|
||||
You: /opsx:verify
|
||||
|
||||
AI: Verifying add-dark-mode...
|
||||
|
||||
✓ All tasks in tasks.md are checked
|
||||
✓ Requirements in specs have corresponding code
|
||||
✓ Design decisions are reflected in implementation
|
||||
|
||||
Ready to archive!
|
||||
```
|
||||
|
||||
Catches mismatches before you close out the change.
|
||||
|
||||
### Name Changes Clearly
|
||||
|
||||
Good names make `openspec list` useful:
|
||||
|
||||
```text
|
||||
Good: Avoid:
|
||||
add-dark-mode feature-1
|
||||
fix-login-redirect update
|
||||
optimize-product-query changes
|
||||
implement-2fa wip
|
||||
```
|
||||
|
||||
## Command Quick Reference
|
||||
|
||||
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: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:archive` | Complete the change | All work finished |
|
||||
| `/opsx:bulk-archive` | Archive multiple changes | Parallel work, batch completion |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Commands](commands.md) - Full command reference with options
|
||||
- [Concepts](concepts.md) - Deep dive into specs, artifacts, and schemas
|
||||
- [Customization](customization.md) - Create custom workflows
|
||||
@@ -19,7 +19,7 @@
|
||||
{
|
||||
default = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "openspec";
|
||||
version = "0.20.0";
|
||||
version = "0.23.0";
|
||||
|
||||
src = ./.;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = pkgs.pnpm_9;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-m/7IdY1ou9ljjYAcx3W8AyEJvIZfCBWIWxproQ/INPA=";
|
||||
hash = "sha256-9s2kdvd7svK4hofnD66HkDc86WTQeayfF5y7L2dmjNg=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
|
||||
@@ -1,456 +0,0 @@
|
||||
# OpenSpec Instructions
|
||||
|
||||
Instructions for AI coding assistants using OpenSpec for spec-driven development.
|
||||
|
||||
## TL;DR Quick Checklist
|
||||
|
||||
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
|
||||
- Decide scope: new capability vs modify existing capability
|
||||
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
|
||||
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
|
||||
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
|
||||
- Validate: `openspec validate [change-id] --strict --no-interactive` and fix issues
|
||||
- Request approval: Do not start implementation until proposal is approved
|
||||
|
||||
## Three-Stage Workflow
|
||||
|
||||
### Stage 1: Creating Changes
|
||||
Create proposal when you need to:
|
||||
- Add features or functionality
|
||||
- Make breaking changes (API, schema)
|
||||
- Change architecture or patterns
|
||||
- Optimize performance (changes behavior)
|
||||
- Update security patterns
|
||||
|
||||
Triggers (examples):
|
||||
- "Help me create a change proposal"
|
||||
- "Help me plan a change"
|
||||
- "Help me create a proposal"
|
||||
- "I want to create a spec proposal"
|
||||
- "I want to create a spec"
|
||||
|
||||
Loose matching guidance:
|
||||
- Contains one of: `proposal`, `change`, `spec`
|
||||
- With one of: `create`, `plan`, `make`, `start`, `help`
|
||||
|
||||
Skip proposal for:
|
||||
- Bug fixes (restore intended behavior)
|
||||
- Typos, formatting, comments
|
||||
- Dependency updates (non-breaking)
|
||||
- Configuration changes
|
||||
- Tests for existing behavior
|
||||
|
||||
**Workflow**
|
||||
1. Review `openspec/project.md`, `openspec list`, and `openspec list --specs` to understand current context.
|
||||
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas under `openspec/changes/<id>/`.
|
||||
3. Draft spec deltas using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement.
|
||||
4. Run `openspec validate <id> --strict --no-interactive` and resolve any issues before sharing the proposal.
|
||||
|
||||
### Stage 2: Implementing Changes
|
||||
Track these steps as TODOs and complete them one by one.
|
||||
1. **Read proposal.md** - Understand what's being built
|
||||
2. **Read design.md** (if exists) - Review technical decisions
|
||||
3. **Read tasks.md** - Get implementation checklist
|
||||
4. **Implement tasks sequentially** - Complete in order
|
||||
5. **Confirm completion** - Ensure every item in `tasks.md` is finished before updating statuses
|
||||
6. **Update checklist** - After all work is done, set every task to `- [x]` so the list reflects reality
|
||||
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
|
||||
|
||||
### Stage 3: Archiving Changes
|
||||
After deployment, create separate PR to:
|
||||
- Move `changes/[name]/` → `changes/archive/YYYY-MM-DD-[name]/`
|
||||
- Update `specs/` if capabilities changed
|
||||
- Use `openspec archive <change-id> --skip-specs --yes` for tooling-only changes (always pass the change ID explicitly)
|
||||
- Run `openspec validate --strict --no-interactive` to confirm the archived change passes checks
|
||||
|
||||
## Before Any Task
|
||||
|
||||
**Context Checklist:**
|
||||
- [ ] Read relevant specs in `specs/[capability]/spec.md`
|
||||
- [ ] Check pending changes in `changes/` for conflicts
|
||||
- [ ] Read `openspec/project.md` for conventions
|
||||
- [ ] Run `openspec list` to see active changes
|
||||
- [ ] Run `openspec list --specs` to see existing capabilities
|
||||
|
||||
**Before Creating Specs:**
|
||||
- Always check if capability already exists
|
||||
- Prefer modifying existing specs over creating duplicates
|
||||
- Use `openspec show [spec]` to review current state
|
||||
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
|
||||
|
||||
### Search Guidance
|
||||
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
|
||||
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
|
||||
- Show details:
|
||||
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
|
||||
- Change: `openspec show <change-id> --json --deltas-only`
|
||||
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI Commands
|
||||
|
||||
```bash
|
||||
# Essential commands
|
||||
openspec list # List active changes
|
||||
openspec list --specs # List specifications
|
||||
openspec show [item] # Display change or spec
|
||||
openspec validate [item] # Validate changes or specs
|
||||
openspec archive <change-id> [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
|
||||
|
||||
# Project management
|
||||
openspec init [path] # Initialize OpenSpec
|
||||
openspec update [path] # Update instruction files
|
||||
|
||||
# Interactive mode
|
||||
openspec show # Prompts for selection
|
||||
openspec validate # Bulk validation mode
|
||||
|
||||
# Debugging
|
||||
openspec show [change] --json --deltas-only
|
||||
openspec validate [change] --strict --no-interactive
|
||||
```
|
||||
|
||||
### Command Flags
|
||||
|
||||
- `--json` - Machine-readable output
|
||||
- `--type change|spec` - Disambiguate items
|
||||
- `--strict` - Comprehensive validation
|
||||
- `--no-interactive` - Disable prompts
|
||||
- `--skip-specs` - Archive without spec updates
|
||||
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── project.md # Project conventions
|
||||
├── specs/ # Current truth - what IS built
|
||||
│ └── [capability]/ # Single focused capability
|
||||
│ ├── spec.md # Requirements and scenarios
|
||||
│ └── design.md # Technical patterns
|
||||
├── changes/ # Proposals - what SHOULD change
|
||||
│ ├── [change-name]/
|
||||
│ │ ├── proposal.md # Why, what, impact
|
||||
│ │ ├── tasks.md # Implementation checklist
|
||||
│ │ ├── design.md # Technical decisions (optional; see criteria)
|
||||
│ │ └── specs/ # Delta changes
|
||||
│ │ └── [capability]/
|
||||
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
|
||||
│ └── archive/ # Completed changes
|
||||
```
|
||||
|
||||
## Creating Change Proposals
|
||||
|
||||
### Decision Tree
|
||||
|
||||
```
|
||||
New request?
|
||||
├─ Bug fix restoring spec behavior? → Fix directly
|
||||
├─ Typo/format/comment? → Fix directly
|
||||
├─ New feature/capability? → Create proposal
|
||||
├─ Breaking change? → Create proposal
|
||||
├─ Architecture change? → Create proposal
|
||||
└─ Unclear? → Create proposal (safer)
|
||||
```
|
||||
|
||||
### Proposal Structure
|
||||
|
||||
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
|
||||
|
||||
2. **Write proposal.md:**
|
||||
```markdown
|
||||
# Change: [Brief description of change]
|
||||
|
||||
## Why
|
||||
[1-2 sentences on problem/opportunity]
|
||||
|
||||
## What Changes
|
||||
- [Bullet list of changes]
|
||||
- [Mark breaking changes with **BREAKING**]
|
||||
|
||||
## Impact
|
||||
- Affected specs: [list capabilities]
|
||||
- Affected code: [key files/systems]
|
||||
```
|
||||
|
||||
3. **Create spec deltas:** `specs/[capability]/spec.md`
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: New Feature
|
||||
The system SHALL provide...
|
||||
|
||||
#### Scenario: Success case
|
||||
- **WHEN** user performs action
|
||||
- **THEN** expected result
|
||||
|
||||
## MODIFIED Requirements
|
||||
### Requirement: Existing Feature
|
||||
[Complete modified requirement]
|
||||
|
||||
## REMOVED Requirements
|
||||
### Requirement: Old Feature
|
||||
**Reason**: [Why removing]
|
||||
**Migration**: [How to handle]
|
||||
```
|
||||
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
|
||||
|
||||
4. **Create tasks.md:**
|
||||
```markdown
|
||||
## 1. Implementation
|
||||
- [ ] 1.1 Create database schema
|
||||
- [ ] 1.2 Implement API endpoint
|
||||
- [ ] 1.3 Add frontend component
|
||||
- [ ] 1.4 Write tests
|
||||
```
|
||||
|
||||
5. **Create design.md when needed:**
|
||||
Create `design.md` if any of the following apply; otherwise omit it:
|
||||
- Cross-cutting change (multiple services/modules) or a new architectural pattern
|
||||
- New external dependency or significant data model changes
|
||||
- Security, performance, or migration complexity
|
||||
- Ambiguity that benefits from technical decisions before coding
|
||||
|
||||
Minimal `design.md` skeleton:
|
||||
```markdown
|
||||
## Context
|
||||
[Background, constraints, stakeholders]
|
||||
|
||||
## Goals / Non-Goals
|
||||
- Goals: [...]
|
||||
- Non-Goals: [...]
|
||||
|
||||
## Decisions
|
||||
- Decision: [What and why]
|
||||
- Alternatives considered: [Options + rationale]
|
||||
|
||||
## Risks / Trade-offs
|
||||
- [Risk] → Mitigation
|
||||
|
||||
## Migration Plan
|
||||
[Steps, rollback]
|
||||
|
||||
## Open Questions
|
||||
- [...]
|
||||
```
|
||||
|
||||
## Spec File Format
|
||||
|
||||
### Critical: Scenario Formatting
|
||||
|
||||
**CORRECT** (use #### headers):
|
||||
```markdown
|
||||
#### Scenario: User login success
|
||||
- **WHEN** valid credentials provided
|
||||
- **THEN** return JWT token
|
||||
```
|
||||
|
||||
**WRONG** (don't use bullets or bold):
|
||||
```markdown
|
||||
- **Scenario: User login** ❌
|
||||
**Scenario**: User login ❌
|
||||
### Scenario: User login ❌
|
||||
```
|
||||
|
||||
Every requirement MUST have at least one scenario.
|
||||
|
||||
### Requirement Wording
|
||||
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
|
||||
|
||||
### Delta Operations
|
||||
|
||||
- `## ADDED Requirements` - New capabilities
|
||||
- `## MODIFIED Requirements` - Changed behavior
|
||||
- `## REMOVED Requirements` - Deprecated features
|
||||
- `## RENAMED Requirements` - Name changes
|
||||
|
||||
Headers matched with `trim(header)` - whitespace ignored.
|
||||
|
||||
#### When to use ADDED vs MODIFIED
|
||||
- ADDED: Introduces a new capability or sub-capability that can stand alone as a requirement. Prefer ADDED when the change is orthogonal (e.g., adding "Slash Command Configuration") rather than altering the semantics of an existing requirement.
|
||||
- MODIFIED: Changes the behavior, scope, or acceptance criteria of an existing requirement. Always paste the full, updated requirement content (header + all scenarios). The archiver will replace the entire requirement with what you provide here; partial deltas will drop previous details.
|
||||
- RENAMED: Use when only the name changes. If you also change behavior, use RENAMED (name) plus MODIFIED (content) referencing the new name.
|
||||
|
||||
Common pitfall: Using MODIFIED to add a new concern without including the previous text. This causes loss of detail at archive time. If you aren’t explicitly changing the existing requirement, add a new requirement under ADDED instead.
|
||||
|
||||
Authoring a MODIFIED requirement correctly:
|
||||
1) Locate the existing requirement in `openspec/specs/<capability>/spec.md`.
|
||||
2) Copy the entire requirement block (from `### Requirement: ...` through its scenarios).
|
||||
3) Paste it under `## MODIFIED Requirements` and edit to reflect the new behavior.
|
||||
4) Ensure the header text matches exactly (whitespace-insensitive) and keep at least one `#### Scenario:`.
|
||||
|
||||
Example for RENAMED:
|
||||
```markdown
|
||||
## RENAMED Requirements
|
||||
- FROM: `### Requirement: Login`
|
||||
- TO: `### Requirement: User Authentication`
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
|
||||
**"Change must have at least one delta"**
|
||||
- Check `changes/[name]/specs/` exists with .md files
|
||||
- Verify files have operation prefixes (## ADDED Requirements)
|
||||
|
||||
**"Requirement must have at least one scenario"**
|
||||
- Check scenarios use `#### Scenario:` format (4 hashtags)
|
||||
- Don't use bullet points or bold for scenario headers
|
||||
|
||||
**Silent scenario parsing failures**
|
||||
- Exact format required: `#### Scenario: Name`
|
||||
- Debug with: `openspec show [change] --json --deltas-only`
|
||||
|
||||
### Validation Tips
|
||||
|
||||
```bash
|
||||
# Always use strict mode for comprehensive checks
|
||||
openspec validate [change] --strict --no-interactive
|
||||
|
||||
# Debug delta parsing
|
||||
openspec show [change] --json | jq '.deltas'
|
||||
|
||||
# Check specific requirement
|
||||
openspec show [spec] --json -r 1
|
||||
```
|
||||
|
||||
## Happy Path Script
|
||||
|
||||
```bash
|
||||
# 1) Explore current state
|
||||
openspec spec list --long
|
||||
openspec list
|
||||
# Optional full-text search:
|
||||
# rg -n "Requirement:|Scenario:" openspec/specs
|
||||
# rg -n "^#|Requirement:" openspec/changes
|
||||
|
||||
# 2) Choose change id and scaffold
|
||||
CHANGE=add-two-factor-auth
|
||||
mkdir -p openspec/changes/$CHANGE/{specs/auth}
|
||||
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
|
||||
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
|
||||
|
||||
# 3) Add deltas (example)
|
||||
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
Users MUST provide a second factor during login.
|
||||
|
||||
#### Scenario: OTP required
|
||||
- **WHEN** valid credentials are provided
|
||||
- **THEN** an OTP challenge is required
|
||||
EOF
|
||||
|
||||
# 4) Validate
|
||||
openspec validate $CHANGE --strict --no-interactive
|
||||
```
|
||||
|
||||
## Multi-Capability Example
|
||||
|
||||
```
|
||||
openspec/changes/add-2fa-notify/
|
||||
├── proposal.md
|
||||
├── tasks.md
|
||||
└── specs/
|
||||
├── auth/
|
||||
│ └── spec.md # ADDED: Two-Factor Authentication
|
||||
└── notifications/
|
||||
└── spec.md # ADDED: OTP email notification
|
||||
```
|
||||
|
||||
auth/spec.md
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: Two-Factor Authentication
|
||||
...
|
||||
```
|
||||
|
||||
notifications/spec.md
|
||||
```markdown
|
||||
## ADDED Requirements
|
||||
### Requirement: OTP Email Notification
|
||||
...
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Simplicity First
|
||||
- Default to <100 lines of new code
|
||||
- Single-file implementations until proven insufficient
|
||||
- Avoid frameworks without clear justification
|
||||
- Choose boring, proven patterns
|
||||
|
||||
### Complexity Triggers
|
||||
Only add complexity with:
|
||||
- Performance data showing current solution too slow
|
||||
- Concrete scale requirements (>1000 users, >100MB data)
|
||||
- Multiple proven use cases requiring abstraction
|
||||
|
||||
### Clear References
|
||||
- Use `file.ts:42` format for code locations
|
||||
- Reference specs as `specs/auth/spec.md`
|
||||
- Link related changes and PRs
|
||||
|
||||
### Capability Naming
|
||||
- Use verb-noun: `user-auth`, `payment-capture`
|
||||
- Single purpose per capability
|
||||
- 10-minute understandability rule
|
||||
- Split if description needs "AND"
|
||||
|
||||
### Change ID Naming
|
||||
- Use kebab-case, short and descriptive: `add-two-factor-auth`
|
||||
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
|
||||
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
|
||||
|
||||
## Tool Selection Guide
|
||||
|
||||
| Task | Tool | Why |
|
||||
|------|------|-----|
|
||||
| Find files by pattern | Glob | Fast pattern matching |
|
||||
| Search code content | Grep | Optimized regex search |
|
||||
| Read specific files | Read | Direct file access |
|
||||
| Explore unknown scope | Task | Multi-step investigation |
|
||||
|
||||
## Error Recovery
|
||||
|
||||
### Change Conflicts
|
||||
1. Run `openspec list` to see active changes
|
||||
2. Check for overlapping specs
|
||||
3. Coordinate with change owners
|
||||
4. Consider combining proposals
|
||||
|
||||
### Validation Failures
|
||||
1. Run with `--strict` flag
|
||||
2. Check JSON output for details
|
||||
3. Verify spec file format
|
||||
4. Ensure scenarios properly formatted
|
||||
|
||||
### Missing Context
|
||||
1. Read project.md first
|
||||
2. Check related specs
|
||||
3. Review recent archives
|
||||
4. Ask for clarification
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Stage Indicators
|
||||
- `changes/` - Proposed, not yet built
|
||||
- `specs/` - Built and deployed
|
||||
- `archive/` - Completed changes
|
||||
|
||||
### File Purposes
|
||||
- `proposal.md` - Why and what
|
||||
- `tasks.md` - Implementation steps
|
||||
- `design.md` - Technical decisions
|
||||
- `spec.md` - Requirements and behavior
|
||||
|
||||
### CLI Essentials
|
||||
```bash
|
||||
openspec list # What's in progress?
|
||||
openspec show [item] # View details
|
||||
openspec validate --strict --no-interactive # Is it correct?
|
||||
openspec archive <change-id> [--yes|-y] # Mark complete (add --yes for automation)
|
||||
```
|
||||
|
||||
Remember: Specs are truth. Changes are proposals. Keep them in sync.
|
||||
@@ -0,0 +1,136 @@
|
||||
# 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,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-01-24
|
||||
@@ -0,0 +1,115 @@
|
||||
## Context
|
||||
|
||||
OpenSpec has a complete skill and slash command generation system. Skills are defined in `src/core/templates/skill-templates.ts` as functions that return `SkillTemplate` objects (for Agent Skills) and `CommandTemplate` objects (for slash commands). These are registered in `src/core/shared/skill-generation.ts` and generated during `openspec init` and `openspec update`.
|
||||
|
||||
Existing skills follow a consistent pattern:
|
||||
- `getXxxSkillTemplate()` returns the skill with name, description, instructions
|
||||
- `getOpsxXxxCommandTemplate()` returns the slash command with name, description, category, tags, content
|
||||
- Both are registered in their respective arrays in `skill-generation.ts`
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Add `/opsx:onboard` skill that teaches the OpenSpec workflow through guided practice
|
||||
- Follow existing patterns for skill/command template generation
|
||||
- Provide comprehensive narration that explains each step
|
||||
- Include codebase analysis to suggest real, appropriately-scoped tasks
|
||||
|
||||
**Non-Goals:**
|
||||
- Creating a separate "demo mode" or simulated workflow (we do real work)
|
||||
- Adding new CLI commands (this is purely agent instructions)
|
||||
- Modifying the init/update flow (just adding to the template arrays)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: Single Monolithic Skill
|
||||
|
||||
The onboard skill will be a single comprehensive instruction set rather than composing existing skills with flags.
|
||||
|
||||
**Rationale:**
|
||||
- Slash commands don't support flags (they're just prompts)
|
||||
- A monolithic skill gives complete control over narration and pacing
|
||||
- Easier to maintain a single cohesive experience
|
||||
- Users learn the real commands by seeing them mentioned in narration
|
||||
|
||||
### Decision 2: Codebase Analysis Patterns
|
||||
|
||||
The skill instructions will direct the agent to look for specific patterns when suggesting starter tasks:
|
||||
|
||||
1. TODO/FIXME comments in code
|
||||
2. Missing error handling (`catch` blocks that swallow errors, no try-catch around risky operations)
|
||||
3. Functions without tests (cross-reference src/ with test files)
|
||||
4. Type: `any` in TypeScript files
|
||||
5. Console.log statements in non-debug code
|
||||
6. Missing input validation on user-facing inputs
|
||||
7. Recent git commits (for context on what user is working on)
|
||||
|
||||
**Rationale:** These are universally applicable, easy to detect, and produce well-scoped tasks.
|
||||
|
||||
### Decision 3: Narration Integration Style
|
||||
|
||||
Each phase will follow a pattern:
|
||||
1. **EXPLAIN** what we're about to do and why (1-2 sentences)
|
||||
2. **DO** the action (run command, create artifact)
|
||||
3. **SHOW** what happened
|
||||
4. **PAUSE** at key transitions (not every step)
|
||||
|
||||
Pauses occur at:
|
||||
- After task selection (before creating change)
|
||||
- After drafting proposal (before saving)
|
||||
- After tasks are generated (before implementation)
|
||||
- After archive (final recap)
|
||||
|
||||
**Rationale:** Too many pauses becomes tedious. Too few loses the teaching opportunity. These are the natural "chapter breaks."
|
||||
|
||||
### Decision 4: Scope Guardrail Approach
|
||||
|
||||
When user selects a task that's too large, the skill will:
|
||||
1. Acknowledge the task is valuable
|
||||
2. Explain why smaller is better for first time
|
||||
3. Suggest a smaller slice or alternative
|
||||
4. Let user override if they insist
|
||||
|
||||
**Rationale:** Soft guardrails teach without frustrating. Users learn scope calibration as part of the experience.
|
||||
|
||||
### Decision 5: Template Structure
|
||||
|
||||
The skill template will be ~400-600 lines of instruction text, structured as:
|
||||
|
||||
```
|
||||
- Preflight checks (init status)
|
||||
- Phase 1: Welcome & Setup
|
||||
- Phase 2: Task Selection (with codebase analysis instructions)
|
||||
- Phase 3: Explore Demo (brief)
|
||||
- Phase 4: Change Creation
|
||||
- Phase 5: Proposal
|
||||
- Phase 6: Specs
|
||||
- Phase 7: Design
|
||||
- Phase 8: Tasks
|
||||
- Phase 9: Apply (Implementation)
|
||||
- Phase 10: Archive
|
||||
- Phase 11: Recap & Next Steps
|
||||
- Edge cases & graceful exits
|
||||
```
|
||||
|
||||
The command template will be identical to the skill template (same content, different wrapper).
|
||||
|
||||
**Rationale:** Following the established pattern where skill and command share the same core instructions.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**Risk: Instruction length**
|
||||
The skill will be significantly longer than existing skills (~500 lines vs ~100-200).
|
||||
→ Mitigation: This is acceptable since onboarding is inherently comprehensive. Token cost is one-time per session.
|
||||
|
||||
**Risk: Codebase analysis may find nothing**
|
||||
Some codebases (new projects, very clean code) may not have obvious improvement opportunities.
|
||||
→ Mitigation: Fall back to asking user what they want to build. Include "add a new feature" as an option.
|
||||
|
||||
**Risk: Task suggestions may be inappropriate**
|
||||
Agent might suggest tasks that touch sensitive code or have hidden complexity.
|
||||
→ Mitigation: User always chooses; agent just suggests. Scope estimates help set expectations.
|
||||
|
||||
**Risk: User abandons mid-way**
|
||||
Onboarding takes ~15 minutes; users may not complete it.
|
||||
→ Mitigation: Graceful exit handling - note the change is saved, explain how to continue later.
|
||||
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
Users who run `openspec init` are left with files but no clear path to actually using the system. There's a gap between "I have OpenSpec set up" and "I understand the workflow." An onboarding skill would guide users through their first complete change cycle on a real task in their codebase, teaching the workflow by doing it.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add new `/opsx:onboard` skill that guides users through their first OpenSpec change
|
||||
- Add corresponding slash command template for editor integrations
|
||||
- The skill will:
|
||||
- Analyze the user's codebase to suggest appropriately-scoped starter tasks
|
||||
- Walk through the full workflow (explore → new → proposal → specs → design → tasks → apply → archive)
|
||||
- Provide narration explaining each step as it happens
|
||||
- Result in a real, implemented change in the user's codebase
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `opsx-onboard-skill`: The onboarding skill that guides users through their first complete OpenSpec workflow cycle with narration and codebase-aware task suggestions
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- No existing specs are being modified - this is purely additive -->
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/templates/skill-templates.ts`: Add `getOnboardSkillTemplate()` and `getOpsxOnboardCommandTemplate()` functions
|
||||
- `src/core/shared/skill-generation.ts`: Register the new skill and command templates in `getSkillTemplates()` and `getCommandTemplates()`
|
||||
- Users running `openspec init` or `openspec update` will get the new skill/command files generated
|
||||
@@ -0,0 +1,162 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: OPSX Onboard Skill
|
||||
|
||||
The system SHALL provide an `/opsx:onboard` skill that guides users through their first complete OpenSpec workflow cycle with narration and real codebase work.
|
||||
|
||||
#### Scenario: Skill invocation
|
||||
|
||||
- **WHEN** user invokes `/opsx:onboard`
|
||||
- **THEN** agent checks if OpenSpec is initialized
|
||||
- **AND** if not initialized, prompts user to run `openspec init` first
|
||||
- **AND** if initialized, proceeds with onboarding flow
|
||||
|
||||
#### Scenario: Welcome and expectations
|
||||
|
||||
- **WHEN** onboarding begins
|
||||
- **THEN** agent displays welcome message explaining what will happen
|
||||
- **AND** sets expectation of ~15 minute duration
|
||||
- **AND** explains the workflow phases: explore → new → artifacts → apply → archive
|
||||
|
||||
### Requirement: Codebase Analysis for Task Suggestions
|
||||
|
||||
The skill SHALL analyze the user's codebase to suggest appropriately-scoped starter tasks.
|
||||
|
||||
#### Scenario: Codebase scanning
|
||||
|
||||
- **WHEN** onboarding reaches task selection phase
|
||||
- **THEN** agent scans codebase for small improvement opportunities
|
||||
- **AND** looks for: TODO/FIXME comments, missing error handling, functions without tests, outdated dependencies, type: any in TypeScript, console.log in production code, missing input validation
|
||||
- **AND** checks recent git commits for context on current work
|
||||
|
||||
#### Scenario: Task suggestion presentation
|
||||
|
||||
- **WHEN** agent has analyzed codebase
|
||||
- **THEN** agent presents 3-4 specific task suggestions with scope estimates
|
||||
- **AND** each suggestion includes: task description, estimated scope (files/lines), why it's a good starter
|
||||
- **AND** offers option for user to specify their own task
|
||||
|
||||
#### Scenario: Scope guardrail
|
||||
|
||||
- **WHEN** user selects or describes a task that is too large
|
||||
- **THEN** agent gently redirects toward smaller scope
|
||||
- **AND** suggests breaking down or deferring the large task
|
||||
- **AND** offers appropriately-sized alternatives
|
||||
|
||||
### Requirement: Explore Phase Demo
|
||||
|
||||
The skill SHALL briefly demonstrate explore mode before creating a change.
|
||||
|
||||
#### Scenario: Brief explore demonstration
|
||||
|
||||
- **WHEN** task is selected
|
||||
- **THEN** agent briefly demonstrates `/opsx:explore` by investigating relevant code
|
||||
- **AND** explains explore mode is for thinking before doing
|
||||
- **AND** keeps this phase short (not a full exploration session)
|
||||
- **AND** transitions to change creation
|
||||
|
||||
### Requirement: Guided Artifact Creation
|
||||
|
||||
The skill SHALL guide users through each artifact with narration explaining the purpose.
|
||||
|
||||
#### Scenario: Change creation with narration
|
||||
|
||||
- **WHEN** creating the change directory
|
||||
- **THEN** agent runs `openspec new change "<name>"` with derived kebab-case name
|
||||
- **AND** explains what a "change" is (container for thinking and planning)
|
||||
- **AND** shows the folder structure that was created
|
||||
- **AND** pauses for user acknowledgment before proceeding
|
||||
|
||||
#### Scenario: Proposal creation with narration
|
||||
|
||||
- **WHEN** creating proposal.md
|
||||
- **THEN** agent explains proposals capture WHY we're making this change
|
||||
- **AND** drafts proposal based on selected task
|
||||
- **AND** shows draft to user for approval before saving
|
||||
- **AND** explains the sections (Why, What Changes, Capabilities, Impact)
|
||||
|
||||
#### Scenario: Specs creation with narration
|
||||
|
||||
- **WHEN** creating spec files
|
||||
- **THEN** agent explains specs define WHAT we're building in detail
|
||||
- **AND** explains the requirement/scenario format
|
||||
- **AND** creates spec file(s) based on proposal capabilities
|
||||
- **AND** notes that specs become documentation that stays in sync
|
||||
|
||||
#### Scenario: Design creation with narration
|
||||
|
||||
- **WHEN** creating design.md
|
||||
- **THEN** agent explains design captures HOW we'll build it
|
||||
- **AND** notes this is where technical decisions and tradeoffs live
|
||||
- **AND** for small changes, acknowledges design may be brief
|
||||
- **AND** creates design based on proposal and specs
|
||||
|
||||
#### Scenario: Tasks creation with narration
|
||||
|
||||
- **WHEN** creating tasks.md
|
||||
- **THEN** agent explains tasks break work into checkboxes
|
||||
- **AND** explains these drive the apply phase
|
||||
- **AND** generates task list from design and specs
|
||||
- **AND** shows tasks and asks if ready to implement
|
||||
|
||||
### Requirement: Guided Implementation
|
||||
|
||||
The skill SHALL implement tasks with narration connecting back to artifacts.
|
||||
|
||||
#### Scenario: Implementation with narration
|
||||
|
||||
- **WHEN** implementing tasks
|
||||
- **THEN** agent announces each task before working on it
|
||||
- **AND** implements the change in the codebase
|
||||
- **AND** occasionally references how specs/design informed decisions
|
||||
- **AND** marks each task complete as it finishes
|
||||
- **AND** keeps narration light (not over-explaining)
|
||||
|
||||
#### Scenario: Implementation completion
|
||||
|
||||
- **WHEN** all tasks are complete
|
||||
- **THEN** agent announces completion
|
||||
- **AND** summarizes what was done
|
||||
- **AND** transitions to archive phase
|
||||
|
||||
### Requirement: Archive with Explanation
|
||||
|
||||
The skill SHALL archive the completed change and explain what happened.
|
||||
|
||||
#### Scenario: Archive with narration
|
||||
|
||||
- **WHEN** archiving the change
|
||||
- **THEN** agent explains archive moves change to dated folder
|
||||
- **AND** runs archive process
|
||||
- **AND** shows where archived change lives
|
||||
- **AND** explains the long-term value (finding decisions later)
|
||||
|
||||
### Requirement: Recap and Next Steps
|
||||
|
||||
The skill SHALL conclude with a recap and command reference.
|
||||
|
||||
#### Scenario: Final recap
|
||||
|
||||
- **WHEN** onboarding is complete
|
||||
- **THEN** agent summarizes the workflow phases completed
|
||||
- **AND** emphasizes this rhythm works for any size change
|
||||
- **AND** provides command reference table (/opsx:explore, /opsx:new, /opsx:ff, /opsx:continue, /opsx:apply, /opsx:verify, /opsx:archive)
|
||||
- **AND** suggests next actions (try /opsx:new or /opsx:ff on something)
|
||||
|
||||
### Requirement: Graceful Exit Handling
|
||||
|
||||
The skill SHALL handle users who want to stop mid-way.
|
||||
|
||||
#### Scenario: User wants to stop
|
||||
|
||||
- **WHEN** user indicates they want to stop during onboarding
|
||||
- **THEN** agent acknowledges gracefully
|
||||
- **AND** notes that the in-progress change is saved
|
||||
- **AND** explains how to continue later with `/opsx:continue <name>`
|
||||
- **AND** exits without pressure
|
||||
|
||||
#### Scenario: User wants quick reference only
|
||||
|
||||
- **WHEN** user says they just want to see the commands
|
||||
- **THEN** agent provides command cheat sheet
|
||||
- **AND** exits gracefully with encouragement to try `/opsx:new`
|
||||
@@ -0,0 +1,21 @@
|
||||
## 1. Add Skill Template
|
||||
|
||||
- [x] 1.1 Add `getOnboardSkillTemplate()` function to `src/core/templates/skill-templates.ts` with full onboarding instruction text covering all phases (preflight, welcome, task selection, explore demo, change creation, proposal, specs, design, tasks, apply, archive, recap)
|
||||
- [x] 1.2 Include codebase analysis instructions for suggesting starter tasks (TODO/FIXME, missing error handling, missing tests, type:any, console.log, missing validation)
|
||||
- [x] 1.3 Include narration pattern instructions (EXPLAIN → DO → SHOW → PAUSE at key transitions)
|
||||
- [x] 1.4 Include scope guardrail instructions for redirecting users away from overly large tasks
|
||||
- [x] 1.5 Include graceful exit handling instructions (user stops mid-way, user just wants command reference)
|
||||
|
||||
## 2. Add Command Template
|
||||
|
||||
- [x] 2.1 Add `getOpsxOnboardCommandTemplate()` function to `src/core/templates/skill-templates.ts` returning CommandTemplate with same instruction content as skill
|
||||
|
||||
## 3. Register Templates
|
||||
|
||||
- [x] 3.1 Add onboard skill to `getSkillTemplates()` array in `src/core/shared/skill-generation.ts` with dirName `openspec-onboard`
|
||||
- [x] 3.2 Add onboard command to `getCommandTemplates()` array in `src/core/shared/skill-generation.ts` with id `onboard`
|
||||
|
||||
## 4. Verify
|
||||
|
||||
- [x] 4.1 Run `pnpm run build` to ensure TypeScript compiles
|
||||
- [x] 4.2 Test skill generation by running `openspec init` in a test directory and verifying onboard skill/command files are created
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-01-23
|
||||
@@ -0,0 +1,193 @@
|
||||
## Context
|
||||
|
||||
Currently `openspec init` and `openspec experimental` are separate commands with distinct purposes:
|
||||
|
||||
- **init**: Creates `openspec/` directory, generates `AGENTS.md`/`project.md`, configures tool config files (`CLAUDE.md`, etc.), generates old slash commands (`/openspec:proposal`, etc.)
|
||||
- **experimental**: Generates skills (9 per tool), generates opsx slash commands (`/opsx:new`, etc.), creates `config.yaml`
|
||||
|
||||
The skill-based workflow (experimental) is the direction we're going, so we're making it the default by merging into `init`.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Single `openspec init` command that sets up the complete skill-based workflow
|
||||
- Clean migration path for existing users with legacy artifacts
|
||||
- Remove all code related to config files and old slash commands
|
||||
- Keep the polished UX from experimental (animated welcome, searchable multi-select)
|
||||
|
||||
**Non-Goals:**
|
||||
- Supporting both workflows simultaneously
|
||||
- Providing options to use the old workflow
|
||||
- Backward compatibility for `/openspec:*` commands (breaking change)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: Merge into init, not into experimental
|
||||
|
||||
**Choice**: Rewrite `init` to do what `experimental` does, then delete `experimental`.
|
||||
|
||||
**Rationale**: `init` is the canonical setup command. Users expect `init` to set up their project. `experimental` was always meant to be temporary.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Keep `experimental` as the main command → confusing name for default behavior
|
||||
- Create new command → unnecessary, `init` already exists
|
||||
|
||||
### Decision 2: Legacy cleanup with Y/N prompt
|
||||
|
||||
**Choice**: Detect legacy artifacts, show what was found, prompt `"Legacy files detected. Upgrade and clean up? [Y/n]"`, then remove if confirmed.
|
||||
|
||||
**Rationale**: Users should know what's being removed. A single Y/N is simple and decisive. No need for multiple options.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Multiple options (keep/remove/cancel) → overcomplicated
|
||||
- Silent removal → users might be surprised
|
||||
- Just warn without removing → leaves cruft
|
||||
|
||||
### Decision 3: Surgical removal of legacy content
|
||||
|
||||
**Choice**: For files with mixed content (OpenSpec markers + user content), only remove the OpenSpec marker block. For files that are 100% OpenSpec content, delete the entire file.
|
||||
|
||||
**Rationale**: Respects user customizations. CLAUDE.md might have other instructions beyond OpenSpec.
|
||||
|
||||
**Edge cases**:
|
||||
- **Config files with mixed content**: Remove only `<!-- OPENSPEC:START -->` to `<!-- OPENSPEC:END -->` block
|
||||
- **Config files that are 100% OpenSpec**: Delete file entirely (check if content outside markers is empty/whitespace)
|
||||
- **Old slash command directories** (`.claude/commands/openspec/`): Delete entire directory (ours)
|
||||
- **`openspec/AGENTS.md`**: Delete (ours)
|
||||
- **Root `AGENTS.md`**: Only remove OpenSpec marker block, preserve rest
|
||||
|
||||
### Decision 6: Preserve project.md with migration hint
|
||||
|
||||
**Choice**: Do NOT auto-delete `openspec/project.md`. Preserve it and show a message directing users to manually migrate content to `config.yaml`'s `context:` field.
|
||||
|
||||
**Rationale**:
|
||||
- `project.md` may contain valuable user-written project documentation
|
||||
- The new workflow uses `config.yaml.context` for the same purpose (auto-injected into artifacts)
|
||||
- Auto-deleting would lose user content; auto-migrating is complex (needs LLM to compress)
|
||||
- Users can migrate manually or use `/opsx:explore` to get AI assistance
|
||||
|
||||
**Migration path**:
|
||||
1. During legacy cleanup, detect `openspec/project.md` but do not delete
|
||||
2. Show in output: "openspec/project.md still exists - migrate content to config.yaml's context: field, then delete"
|
||||
3. User migrates manually or asks Claude in explore mode: "help me migrate project.md to config.yaml"
|
||||
4. User deletes project.md when ready
|
||||
|
||||
**Why not auto-migrate?**
|
||||
- `project.md` is verbose (sections, headers, placeholders)
|
||||
- `config.yaml.context` should be concise and dense
|
||||
- LLM compression would be ideal but adds complexity and non-determinism to init
|
||||
- Manual migration lets users decide what's actually important
|
||||
|
||||
### Decision 4: Hidden alias for experimental
|
||||
|
||||
**Choice**: Keep `openspec experimental` as a hidden command that delegates to `init`.
|
||||
|
||||
**Rationale**: Users who learned `experimental` can still use it during transition. Hidden means it won't show in help.
|
||||
|
||||
### Decision 5: Reuse existing infrastructure
|
||||
|
||||
**Choice**: Reuse skill templates, command adapters, welcome screen, and multi-select from experimental.
|
||||
|
||||
**Rationale**: Already built and working. Just needs to be called from init instead of experimental.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Users with custom `/openspec:*` commands lose them | Document in release notes; old commands are in git history |
|
||||
| Mixed-content detection might be imperfect | Conservative approach: if unsure, preserve the file and warn |
|
||||
| Users confused by missing config files | Clear messaging in init output about what changed |
|
||||
| `openspec update` might break | Review and update `update` command to work with new structure |
|
||||
|
||||
## Architecture
|
||||
|
||||
### What init creates (after merge)
|
||||
|
||||
```
|
||||
openspec/
|
||||
├── config.yaml # Schema settings (from experimental)
|
||||
├── specs/ # Empty, for user's specs
|
||||
└── changes/ # Empty, for user's changes
|
||||
└── archive/
|
||||
|
||||
.<tool>/skills/ # 9 skills per selected tool
|
||||
├── 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
|
||||
|
||||
.<tool>/commands/opsx/ # 9 slash commands per selected tool
|
||||
├── explore.md
|
||||
├── new.md
|
||||
├── continue.md
|
||||
├── apply.md
|
||||
├── ff.md
|
||||
├── verify.md
|
||||
├── sync.md
|
||||
├── archive.md
|
||||
└── bulk-archive.md
|
||||
```
|
||||
|
||||
### What init no longer creates
|
||||
|
||||
- `CLAUDE.md`, `.cursorrules`, `.windsurfrules`, etc. (config files)
|
||||
- `openspec/AGENTS.md`
|
||||
- `openspec/project.md`
|
||||
- Root `AGENTS.md` stub
|
||||
- `.claude/commands/openspec/` (old slash commands)
|
||||
|
||||
### Legacy detection targets
|
||||
|
||||
| Artifact Type | Detection Method | Removal Method |
|
||||
|--------------|------------------|----------------|
|
||||
| Config files (CLAUDE.md, etc.) | File exists AND contains OpenSpec markers | Remove marker block; delete file if empty after |
|
||||
| Old slash command dirs | Directory exists at `.<tool>/commands/openspec/` | Delete entire directory |
|
||||
| openspec/AGENTS.md | File exists at `openspec/AGENTS.md` | Delete file |
|
||||
| openspec/project.md | File exists at `openspec/project.md` | **Preserve** - show migration hint only |
|
||||
| Root AGENTS.md | File exists at `AGENTS.md` AND contains OpenSpec markers | Remove marker block; delete file if empty after |
|
||||
|
||||
### Code to remove
|
||||
|
||||
- `src/core/configurators/` - entire directory (ToolRegistry, all config generators)
|
||||
- `src/core/configurators/slash/` - entire directory (SlashCommandRegistry, old command generators)
|
||||
- `src/core/templates/slash-command-templates.ts` - old `/openspec:*` content
|
||||
- `src/core/templates/claude-template.ts`
|
||||
- `src/core/templates/cline-template.ts`
|
||||
- `src/core/templates/costrict-template.ts`
|
||||
- `src/core/templates/agents-template.ts`
|
||||
- `src/core/templates/agents-root-stub.ts`
|
||||
- `src/core/templates/project-template.ts`
|
||||
- `src/commands/experimental/` - entire directory (merged into init)
|
||||
- Related test files
|
||||
|
||||
### Code to migrate into init
|
||||
|
||||
- Animated welcome screen (`src/ui/welcome-screen.ts`) - keep, call from init
|
||||
- Searchable multi-select (`src/prompts/searchable-multi-select.ts`) - keep, call from init
|
||||
- Skill templates (`src/core/templates/skill-templates.ts`) - keep
|
||||
- Command generation (`src/core/command-generation/`) - keep
|
||||
- Tool states detection (from `experimental/setup.ts`) - move to init
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **What happens to `openspec update`?** - RESOLVED
|
||||
|
||||
**Current behavior**: Updates `openspec/AGENTS.md`, config files (`CLAUDE.md`, etc.) via `ToolRegistry`, and old slash commands (`/openspec:*`) via `SlashCommandRegistry`.
|
||||
|
||||
**New behavior**: Rewrite to refresh skills and opsx commands instead:
|
||||
- Detect which tools have skills installed (check for `.claude/skills/openspec-*/`, etc.)
|
||||
- Refresh all 9 skill files per installed tool using `skill-templates.ts`
|
||||
- Refresh all 9 opsx command files per installed tool using `command-generation/` adapters
|
||||
- Remove imports of `ToolRegistry`, `SlashCommandRegistry`, `agentsTemplate`
|
||||
- Update output messaging to reflect skills/commands instead of config files
|
||||
|
||||
**Key principle**: Same as current update - only refresh existing tools, don't add new ones.
|
||||
|
||||
2. **Should we keep `openspec schemas` and other experimental subcommands?** - RESOLVED
|
||||
|
||||
**Decision**: Yes, keep them. Remove "[Experimental]" label from all subcommands (status, instructions, schemas, etc.). See task 4.3.
|
||||
@@ -0,0 +1,32 @@
|
||||
## Why
|
||||
|
||||
The current setup has two separate commands (`openspec init` and `openspec experimental`) that configure different parts of the OpenSpec workflow. This creates confusion about which command to run, results in partial setups, and maintains two parallel systems (config files + old slash commands vs skills + opsx commands). Making the skill-based workflow the default simplifies onboarding and establishes a single, consistent way to use OpenSpec.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: `openspec init` now generates skills and `/opsx:*` commands instead of config files and `/openspec:*` commands
|
||||
- **BREAKING**: Config files (`CLAUDE.md`, `.cursorrules`, etc.) are no longer generated
|
||||
- **BREAKING**: Old slash commands (`/openspec:proposal`, `/openspec:apply`, `/openspec:archive`) are no longer generated
|
||||
- **BREAKING**: `openspec/AGENTS.md` and `openspec/project.md` are no longer generated
|
||||
- Merge `experimental` command functionality into `init`
|
||||
- Add legacy detection and auto-cleanup with Y/N confirmation
|
||||
- Keep `openspec experimental` as hidden alias for backward compatibility
|
||||
- Use the animated welcome screen from experimental for the unified init
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `legacy-cleanup`: Detect and remove legacy OpenSpec artifacts (config files, old slash commands, AGENTS.md) during init
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `cli-init`: Complete rewrite - generates skills and opsx commands instead of config files and old slash commands; removes AGENTS.md/project.md generation; adds legacy cleanup; uses experimental's animated welcome screen
|
||||
|
||||
## Impact
|
||||
|
||||
- **Code removal**: `ToolRegistry`, `SlashCommandRegistry`, config file generators, old slash command templates, AGENTS.md/project.md templates
|
||||
- **Code migration**: Move skill generation and command adapter logic from `experimental/setup.ts` into `init.ts`
|
||||
- **Commands affected**: `init` (rewritten), `experimental` (becomes hidden alias), `update` (may need adjustment)
|
||||
- **User migration**: Existing users running `init` will be prompted to clean up legacy files
|
||||
- **Breaking for**: Users relying on config files for passive triggering, users using `/openspec:*` commands
|
||||
@@ -0,0 +1,176 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Directory Creation
|
||||
|
||||
The command SHALL create the OpenSpec directory structure with config file.
|
||||
|
||||
#### Scenario: Creating OpenSpec structure
|
||||
|
||||
- **WHEN** `openspec init` is executed
|
||||
- **THEN** create the following directory structure:
|
||||
```
|
||||
openspec/
|
||||
├── config.yaml
|
||||
├── specs/
|
||||
└── changes/
|
||||
└── archive/
|
||||
```
|
||||
|
||||
### Requirement: AI Tool Configuration
|
||||
|
||||
The command SHALL configure AI coding assistants with skills and slash commands using a searchable multi-select experience.
|
||||
|
||||
#### Scenario: Prompting for AI tool selection
|
||||
|
||||
- **WHEN** run interactively
|
||||
- **THEN** display animated welcome screen with OpenSpec logo
|
||||
- **AND** present a searchable multi-select that shows all available tools
|
||||
- **AND** mark already configured tools with "(configured ✓)" indicator
|
||||
- **AND** pre-select configured tools for easy refresh
|
||||
- **AND** sort configured tools to appear first in the list
|
||||
- **AND** allow filtering by typing to search
|
||||
|
||||
#### Scenario: Selecting tools to configure
|
||||
|
||||
- **WHEN** user selects tools and confirms
|
||||
- **THEN** generate skills in `.<tool>/skills/` directory for each selected tool
|
||||
- **AND** generate slash commands in `.<tool>/commands/opsx/` directory for each selected tool
|
||||
- **AND** create `openspec/config.yaml` with default schema setting
|
||||
|
||||
### 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
|
||||
|
||||
### Requirement: Slash Command Generation
|
||||
|
||||
The command SHALL generate opsx slash commands for selected AI tools.
|
||||
|
||||
#### Scenario: Generating slash commands for a tool
|
||||
|
||||
- **WHEN** a tool 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
|
||||
|
||||
### Requirement: Success Output
|
||||
|
||||
The command SHALL provide clear, actionable next steps upon successful initialization.
|
||||
|
||||
#### Scenario: Displaying success message
|
||||
|
||||
- **WHEN** initialization completes successfully
|
||||
- **THEN** display categorized summary:
|
||||
- "Created: <tools>" for newly configured tools
|
||||
- "Refreshed: <tools>" for already-configured tools that were updated
|
||||
- Count of skills and commands generated
|
||||
- **AND** display getting started section with:
|
||||
- `/opsx:new` - Start a new change
|
||||
- `/opsx:continue` - Create the next artifact
|
||||
- `/opsx:apply` - Implement tasks
|
||||
- **AND** display links to documentation and feedback
|
||||
|
||||
#### Scenario: Displaying restart instruction
|
||||
|
||||
- **WHEN** initialization completes successfully and tools were created or refreshed
|
||||
- **THEN** display instruction to restart IDE for slash commands to take effect
|
||||
|
||||
### Requirement: Config File Generation
|
||||
|
||||
The command SHALL create an OpenSpec config file with schema settings.
|
||||
|
||||
#### Scenario: Creating config.yaml
|
||||
|
||||
- **WHEN** initialization completes
|
||||
- **AND** config.yaml does not exist
|
||||
- **THEN** create `openspec/config.yaml` with default schema setting
|
||||
- **AND** display config location in output
|
||||
|
||||
#### Scenario: Preserving existing config.yaml
|
||||
|
||||
- **WHEN** initialization runs in extend mode
|
||||
- **AND** `openspec/config.yaml` already exists
|
||||
- **THEN** preserve the existing config file
|
||||
- **AND** display "(exists)" indicator in output
|
||||
|
||||
### Requirement: Non-Interactive Mode
|
||||
|
||||
The command SHALL support non-interactive operation through command-line options.
|
||||
|
||||
#### Scenario: Select all tools non-interactively
|
||||
|
||||
- **WHEN** run with `--tools all`
|
||||
- **THEN** automatically select every available AI tool without prompting
|
||||
- **AND** proceed with skill and command generation
|
||||
|
||||
#### Scenario: Select specific tools non-interactively
|
||||
|
||||
- **WHEN** run with `--tools claude,cursor`
|
||||
- **THEN** parse the comma-separated tool IDs
|
||||
- **AND** generate skills and commands for specified tools only
|
||||
|
||||
#### Scenario: Skip tool configuration non-interactively
|
||||
|
||||
- **WHEN** run with `--tools none`
|
||||
- **THEN** create only the openspec directory structure and config.yaml
|
||||
- **AND** skip skill and command generation
|
||||
|
||||
### Requirement: Experimental Command Alias
|
||||
|
||||
The command SHALL maintain backward compatibility with the experimental command.
|
||||
|
||||
#### Scenario: Running openspec experimental
|
||||
|
||||
- **WHEN** user runs `openspec experimental`
|
||||
- **THEN** delegate to `openspec init`
|
||||
- **AND** the command SHALL be hidden from help output
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: File Generation
|
||||
|
||||
**Reason**: AGENTS.md and project.md are no longer generated. Skills contain all necessary instructions.
|
||||
|
||||
**Migration**: Skills in `.<tool>/skills/` provide all OpenSpec workflow instructions. No manual file needed.
|
||||
|
||||
### Requirement: AI Tool Configuration Details
|
||||
|
||||
**Reason**: Config files (CLAUDE.md, .cursorrules, etc.) are replaced by skills.
|
||||
|
||||
**Migration**: Use skills in `.<tool>/skills/` instead of config files. Skills provide richer, tool-specific instructions.
|
||||
|
||||
### Requirement: Slash Command Configuration
|
||||
|
||||
**Reason**: Old `/openspec:*` slash commands are replaced by `/opsx:*` commands with richer functionality.
|
||||
|
||||
**Migration**: Use `/opsx:new`, `/opsx:continue`, `/opsx:apply` instead of `/openspec:proposal`, `/openspec:apply`, `/openspec:archive`.
|
||||
|
||||
### Requirement: Root instruction stub
|
||||
|
||||
**Reason**: Root AGENTS.md stub is no longer needed. Skills provide tool-specific instructions.
|
||||
|
||||
**Migration**: Skills are loaded automatically by supporting tools. No root stub needed.
|
||||
@@ -0,0 +1,158 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Legacy artifact detection
|
||||
|
||||
The system SHALL detect legacy OpenSpec artifacts from previous init versions.
|
||||
|
||||
#### Scenario: Detecting legacy config files
|
||||
|
||||
- **WHEN** running `openspec init` on an existing project
|
||||
- **THEN** the system SHALL check for config files with OpenSpec markers:
|
||||
- `CLAUDE.md`
|
||||
- `.cursorrules`
|
||||
- `.windsurfrules`
|
||||
- `.clinerules`
|
||||
- `.kilocode_rules`
|
||||
- `.github/copilot-instructions.md`
|
||||
- `.amazonq/instructions.md`
|
||||
- `CODEBUDDY.md`
|
||||
- `IFLOW.md`
|
||||
- And all other tool config files from the legacy ToolRegistry
|
||||
|
||||
#### Scenario: Detecting legacy slash command directories
|
||||
|
||||
- **WHEN** running `openspec init` on an existing project
|
||||
- **THEN** the system SHALL check for old slash command directories:
|
||||
- `.claude/commands/openspec/`
|
||||
- `.cursor/commands/openspec/` (note: old format used `openspec-*.md` in commands root)
|
||||
- `.windsurf/workflows/openspec-*.md`
|
||||
- And equivalent directories for all tools in the legacy SlashCommandRegistry
|
||||
|
||||
#### Scenario: Detecting legacy OpenSpec structure files
|
||||
|
||||
- **WHEN** running `openspec init` on an existing project
|
||||
- **THEN** the system SHALL check for:
|
||||
- `openspec/AGENTS.md`
|
||||
- `openspec/project.md` (for migration messaging only, not deleted)
|
||||
- Root `AGENTS.md` with OpenSpec markers
|
||||
|
||||
### Requirement: Legacy cleanup confirmation
|
||||
|
||||
The system SHALL prompt for confirmation before removing legacy artifacts.
|
||||
|
||||
#### Scenario: Prompting for cleanup when legacy detected
|
||||
|
||||
- **WHEN** legacy artifacts are detected
|
||||
- **THEN** the system SHALL display what was found
|
||||
- **AND** prompt: "Legacy files detected. Upgrade and clean up? [Y/n]"
|
||||
- **AND** default to Yes if user presses Enter
|
||||
|
||||
#### Scenario: User confirms cleanup
|
||||
|
||||
- **WHEN** user responds Y or presses Enter
|
||||
- **THEN** the system SHALL remove legacy artifacts
|
||||
- **AND** proceed with skill-based setup
|
||||
|
||||
#### Scenario: User declines cleanup
|
||||
|
||||
- **WHEN** user responds N
|
||||
- **THEN** the system SHALL abort initialization
|
||||
- **AND** display message suggesting manual cleanup or using `--force` flag
|
||||
|
||||
#### Scenario: Non-interactive mode
|
||||
|
||||
- **WHEN** running with `--no-interactive` or in CI environment
|
||||
- **AND** legacy artifacts are detected
|
||||
- **THEN** the system SHALL abort with exit code 1
|
||||
- **AND** display detected legacy artifacts
|
||||
- **AND** suggest running interactively or using `--force` flag
|
||||
|
||||
### Requirement: Surgical removal of config file content
|
||||
|
||||
The system SHALL preserve user content when removing OpenSpec markers from config files.
|
||||
|
||||
#### Scenario: Config file with only OpenSpec content
|
||||
|
||||
- **WHEN** a config file contains only OpenSpec marker block (whitespace outside is acceptable)
|
||||
- **THEN** the system SHALL remove the OpenSpec marker block
|
||||
- **AND** preserve the file (even if empty or whitespace-only)
|
||||
- **AND** NOT delete the file (config files belong to the user's project root)
|
||||
|
||||
#### Scenario: Config file with mixed content
|
||||
|
||||
- **WHEN** a config file contains content outside OpenSpec markers
|
||||
- **THEN** the system SHALL remove only the `<!-- OPENSPEC:START -->` to `<!-- OPENSPEC:END -->` block
|
||||
- **AND** preserve all content before and after the markers
|
||||
- **AND** clean up any resulting double blank lines
|
||||
|
||||
#### Scenario: Root AGENTS.md with mixed content
|
||||
|
||||
- **WHEN** root `AGENTS.md` contains OpenSpec markers AND other content
|
||||
- **THEN** the system SHALL remove only the OpenSpec marker block
|
||||
- **AND** preserve the rest of the file
|
||||
|
||||
### Requirement: Legacy directory removal
|
||||
|
||||
The system SHALL remove legacy slash command directories entirely.
|
||||
|
||||
#### Scenario: Removing old slash command directory
|
||||
|
||||
- **WHEN** a legacy slash command directory exists (e.g., `.claude/commands/openspec/`)
|
||||
- **THEN** the system SHALL delete the entire directory and its contents
|
||||
- **AND** NOT delete the parent directory (e.g., `.claude/commands/` remains)
|
||||
|
||||
#### Scenario: Removing legacy AGENTS.md
|
||||
|
||||
- **WHEN** `openspec/AGENTS.md` exists
|
||||
- **THEN** the system SHALL delete the file
|
||||
- **AND** NOT delete the `openspec/` directory itself
|
||||
|
||||
### Requirement: project.md migration hint
|
||||
|
||||
The system SHALL preserve project.md and display a migration hint instead of deleting it.
|
||||
|
||||
#### Scenario: project.md exists during upgrade
|
||||
|
||||
- **WHEN** `openspec/project.md` exists during legacy cleanup
|
||||
- **THEN** the system SHALL NOT delete the file
|
||||
- **AND** the system SHALL display a migration hint in the output:
|
||||
```
|
||||
Manual migration needed:
|
||||
→ openspec/project.md still exists
|
||||
Move useful content to config.yaml's "context:" field, then delete
|
||||
```
|
||||
|
||||
#### Scenario: project.md migration rationale
|
||||
|
||||
- **GIVEN** project.md may contain user-written project documentation
|
||||
- **AND** config.yaml's context field serves the same purpose (auto-injected into artifacts)
|
||||
- **WHEN** displaying the migration hint
|
||||
- **THEN** users can migrate manually or use `/opsx:explore` to get AI assistance
|
||||
|
||||
### Requirement: Cleanup reporting
|
||||
|
||||
The system SHALL report what was cleaned up.
|
||||
|
||||
#### Scenario: Displaying cleanup summary
|
||||
|
||||
- **WHEN** legacy cleanup completes
|
||||
- **THEN** the system SHALL display a summary section:
|
||||
```
|
||||
Cleaned up legacy files:
|
||||
✓ Removed OpenSpec markers from CLAUDE.md
|
||||
✓ Removed .claude/commands/openspec/ (replaced by /opsx:*)
|
||||
✓ Removed openspec/AGENTS.md (no longer needed)
|
||||
```
|
||||
- **AND IF** `openspec/project.md` exists
|
||||
- **THEN** the system SHALL display a separate migration section:
|
||||
```
|
||||
Manual migration needed:
|
||||
→ openspec/project.md still exists
|
||||
Move useful content to config.yaml's "context:" field, then delete
|
||||
```
|
||||
|
||||
#### Scenario: No legacy detected
|
||||
|
||||
- **WHEN** no legacy artifacts are found
|
||||
- **THEN** the system SHALL NOT display the cleanup section
|
||||
- **AND** proceed directly with skill setup
|
||||
@@ -0,0 +1,67 @@
|
||||
## 1. Legacy Detection & Cleanup Module
|
||||
|
||||
- [x] 1.1 Create `src/core/legacy-cleanup.ts` with detection functions for all legacy artifact types
|
||||
- [x] 1.2 Implement `detectLegacyConfigFiles()` - check for config files with OpenSpec markers
|
||||
- [x] 1.3 Implement `detectLegacySlashCommands()` - check for old `/openspec:*` command directories
|
||||
- [x] 1.4 Implement `detectLegacyStructureFiles()` - check for AGENTS.md (project.md detected separately for messaging)
|
||||
- [x] 1.5 Implement `removeMarkerBlock()` - surgically remove OpenSpec marker blocks from files
|
||||
- [x] 1.6 Implement `cleanupLegacyArtifacts()` - orchestrate removal with proper edge case handling (preserves project.md)
|
||||
- [x] 1.7 Implement migration hint output for project.md - show message directing users to migrate to config.yaml
|
||||
- [x] 1.8 Add unit tests for legacy detection and cleanup functions
|
||||
|
||||
## 2. Rewrite Init Command
|
||||
|
||||
- [x] 2.1 Replace `src/core/init.ts` with new implementation using experimental's approach
|
||||
- [x] 2.2 Import and use animated welcome screen from `src/ui/welcome-screen.ts`
|
||||
- [x] 2.3 Import and use searchable multi-select from `src/prompts/searchable-multi-select.ts`
|
||||
- [x] 2.4 Integrate legacy detection at start of init flow
|
||||
- [x] 2.5 Add Y/N prompt for legacy cleanup confirmation
|
||||
- [x] 2.6 Generate skills using existing `skill-templates.ts`
|
||||
- [x] 2.7 Generate slash commands using existing `command-generation/` adapters
|
||||
- [x] 2.8 Create `openspec/config.yaml` with default schema
|
||||
- [x] 2.9 Update success output to match new workflow (skills, /opsx:* commands)
|
||||
- [x] 2.10 Add `--force` flag to skip legacy cleanup prompt in non-interactive mode
|
||||
|
||||
## 3. Remove Legacy Code
|
||||
|
||||
- [x] 3.1 Delete `src/core/configurators/` directory (ToolRegistry, all config generators)
|
||||
- [x] 3.2 Delete `src/core/templates/slash-command-templates.ts`
|
||||
- [x] 3.3 Delete `src/core/templates/claude-template.ts`
|
||||
- [x] 3.4 Delete `src/core/templates/cline-template.ts`
|
||||
- [x] 3.5 Delete `src/core/templates/costrict-template.ts`
|
||||
- [x] 3.6 Delete `src/core/templates/agents-template.ts`
|
||||
- [x] 3.7 Delete `src/core/templates/agents-root-stub.ts`
|
||||
- [x] 3.8 Delete `src/core/templates/project-template.ts`
|
||||
- [x] 3.9 Delete `src/commands/experimental/` directory
|
||||
- [x] 3.10 Update `src/core/templates/index.ts` to remove deleted exports
|
||||
- [x] 3.11 Delete related test files for removed modules (wizard.ts)
|
||||
|
||||
## 4. Update CLI Registration
|
||||
|
||||
- [x] 4.1 Update `src/cli/index.ts` to remove `registerArtifactWorkflowCommands()` call
|
||||
- [x] 4.2 Keep experimental subcommands (status, instructions, schemas, etc.) but register directly
|
||||
- [x] 4.3 Remove "[Experimental]" labels from kept subcommands
|
||||
- [x] 4.4 Add hidden `experimental` command as alias to `init`
|
||||
|
||||
## 5. Update Related Commands
|
||||
|
||||
- [x] 5.1 Update `openspec update` command to refresh skills/commands instead of config files
|
||||
- [x] 5.2 Remove config file refresh logic from update
|
||||
- [x] 5.3 Add skill refresh logic to update
|
||||
|
||||
## 6. Testing & Verification
|
||||
|
||||
- [x] 6.1 Add integration tests for new init flow (fresh install)
|
||||
- [x] 6.2 Add integration tests for legacy detection and cleanup
|
||||
- [x] 6.3 Add integration tests for extend mode (re-running init)
|
||||
- [x] 6.4 Test non-interactive mode with `--tools` flag
|
||||
- [x] 6.5 Test `--force` flag for CI environments
|
||||
- [x] 6.6 Verify cross-platform path handling (use path.join throughout)
|
||||
- [x] 6.7 Run full test suite and fix any broken tests
|
||||
|
||||
## 7. Documentation & Cleanup
|
||||
|
||||
- [x] 7.1 Update README with new init behavior (skill-based workflow is self-documenting)
|
||||
- [x] 7.2 Document breaking changes for release notes (in tasks file)
|
||||
- [x] 7.3 Remove any orphaned imports/references to deleted modules (verified none exist)
|
||||
- [x] 7.4 Run linter and fix any issues (passed)
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-01-22
|
||||
@@ -0,0 +1,144 @@
|
||||
## Context
|
||||
|
||||
The `artifact-experimental-setup` command generates skill files and opsx slash commands for AI coding assistants. Currently it hardcodes paths to `.claude/skills` and `.claude/commands/opsx`.
|
||||
|
||||
The existing `AI_TOOLS` array in `config.ts` lists 22 AI tools but lacks path information. There's also an existing `SlashCommandConfigurator` system for the old workflow commands, but it's tightly coupled to the old 3 commands (proposal, apply, archive) and can't be easily extended for the 9 opsx commands.
|
||||
|
||||
Each AI tool has:
|
||||
- Different skill directory conventions (`.claude/skills/`, `.cursor/skills/`, etc.)
|
||||
- Different command file paths (`.claude/commands/opsx/`, `.cursor/commands/`, etc.)
|
||||
- Different frontmatter formats (YAML keys, structure varies by tool)
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Support skill generation for any AI tool following the Agent Skills spec
|
||||
- Support command generation with tool-specific formatting via adapters
|
||||
- Require explicit tool selection (no defaults)
|
||||
- Create a generic, extensible command generation system
|
||||
|
||||
**Non-Goals:**
|
||||
- Global path installation (deferred to future work)
|
||||
- Multi-tool generation in single command (future enhancement)
|
||||
- Unifying with existing SlashCommandConfigurator (separate systems for now)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Add `skillsDir` to `AIToolOption` interface
|
||||
|
||||
**Decision**: Add single `skillsDir` field to existing interface. No `commandsDir` or `globalSkillsDir`.
|
||||
|
||||
```typescript
|
||||
interface AIToolOption {
|
||||
name: string;
|
||||
value: string;
|
||||
available: boolean;
|
||||
successLabel?: string;
|
||||
skillsDir?: string; // e.g., '.claude' - /skills suffix per Agent Skills spec
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
- Skills follow Agent Skills spec: `<toolDir>/skills/` - suffix is standard
|
||||
- Commands need per-tool formatting, handled by adapters (not a simple path)
|
||||
- Global paths deferred - can extend interface later
|
||||
|
||||
### 2. Strategy/Adapter pattern for command generation
|
||||
|
||||
**Decision**: Create generic command generation with tool-specific adapters.
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ CommandContent │
|
||||
│ (tool-agnostic: id, name, description, category, tags, body) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ generateCommand(content, adapter) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Claude │ │ Cursor │ │ Windsurf │
|
||||
│ Adapter │ │ Adapter │ │ Adapter │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
```typescript
|
||||
// Tool-agnostic command data
|
||||
interface CommandContent {
|
||||
id: string; // e.g., 'explore', 'new', 'apply'
|
||||
name: string; // e.g., 'OpenSpec Explore'
|
||||
description: string; // e.g., 'Enter explore mode...'
|
||||
category: string; // e.g., 'OpenSpec'
|
||||
tags: string[]; // e.g., ['openspec', 'explore']
|
||||
body: string; // The command instructions
|
||||
}
|
||||
|
||||
// Per-tool formatting strategy
|
||||
interface ToolCommandAdapter {
|
||||
toolId: string;
|
||||
getFilePath(commandId: string): string;
|
||||
formatFile(content: CommandContent): string;
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
- Separates "what to generate" from "how to format it"
|
||||
- Each tool's frontmatter quirks encapsulated in its adapter
|
||||
- Easy to add new tools by implementing adapter interface
|
||||
- Body content shared across all tools
|
||||
|
||||
**Alternative considered**: Extend existing SlashCommandConfigurator
|
||||
- Rejected: Tightly coupled to old 3 commands, significant refactor needed
|
||||
|
||||
### 3. Adapter registry pattern
|
||||
|
||||
**Decision**: Create `CommandAdapterRegistry` similar to existing `SlashCommandRegistry`.
|
||||
|
||||
```typescript
|
||||
class CommandAdapterRegistry {
|
||||
private static adapters: Map<string, ToolCommandAdapter> = new Map();
|
||||
|
||||
static get(toolId: string): ToolCommandAdapter | undefined;
|
||||
static getAll(): ToolCommandAdapter[];
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
- Consistent with existing codebase patterns
|
||||
- Easy lookup by tool ID
|
||||
- Centralized registration
|
||||
|
||||
### 4. Required tool flag
|
||||
|
||||
**Decision**: Require `--tool` flag - error if omitted.
|
||||
|
||||
**Rationale**:
|
||||
- Explicit tool selection avoids assumptions
|
||||
- Consistent with project convention of not providing defaults
|
||||
- Users must consciously choose their target tool
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Adapter maintenance burden** → Each new tool needs an adapter. Mitigated by simple interface - most adapters are ~20 lines.
|
||||
|
||||
**[Risk] Frontmatter format drift** → Tools may change their formats. Mitigated by encapsulating format in adapter - single place to update.
|
||||
|
||||
**[Trade-off] Two command systems** → Old SlashCommandConfigurator and new CommandAdapterRegistry coexist. Acceptable for now - can unify later if needed.
|
||||
|
||||
**[Trade-off] skillsDir optional** → Tools without skillsDir configured will error. Acceptable - we add paths as tools are tested.
|
||||
|
||||
## Implementation Approach
|
||||
|
||||
1. Add `skillsDir` to `AIToolOption` and populate for known tools
|
||||
2. Create `CommandContent` and `ToolCommandAdapter` interfaces
|
||||
3. Implement adapters for Claude, Cursor, Windsurf (start with 3)
|
||||
4. Create `CommandAdapterRegistry`
|
||||
5. Create `generateCommand()` function
|
||||
6. Update `artifact-experimental-setup` to use new system
|
||||
7. Add `--tool` flag with validation
|
||||
@@ -0,0 +1,36 @@
|
||||
## Why
|
||||
|
||||
The `artifact-experimental-setup` command currently hardcodes skill output paths to `.claude/skills` and `.claude/commands/opsx`. This prevents users of other AI coding tools (Cursor, Windsurf, Codex, etc.) from using OpenSpec's skill generation. We need to support the diverse ecosystem of AI coding assistants, each with their own conventions for skill/instruction file locations and command frontmatter formats.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add `skillsDir` path configuration to the existing `AIToolOption` interface in `config.ts`
|
||||
- Add required `--tool <tool-id>` flag to the `artifact-experimental-setup` command
|
||||
- Create a generic command generation system using Strategy/Adapter pattern:
|
||||
- `CommandContent`: tool-agnostic command data (id, name, description, body)
|
||||
- `ToolCommandAdapter`: per-tool formatting (file paths, frontmatter format)
|
||||
- `CommandGenerator`: orchestrates generation using content + adapter
|
||||
- Require explicit tool selection (no default) for clarity
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `ai-tool-paths`: Configuration mapping AI tool IDs to their project-local skill directory paths
|
||||
- `command-generation`: Generic command generation system with tool adapters for formatting differences
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `cli-artifact-workflow`: Adding `--tool` flag to setup command for provider selection
|
||||
|
||||
## Impact
|
||||
|
||||
- **Files Modified**:
|
||||
- `src/core/config.ts` - Extend `AIToolOption` interface with `skillsDir` field
|
||||
- `src/commands/artifact-workflow.ts` - Add `--tool` flag, use provider paths and adapters
|
||||
- **New Files**:
|
||||
- `src/core/command-generation/types.ts` - CommandContent, ToolCommandAdapter interfaces
|
||||
- `src/core/command-generation/generator.ts` - Generic command generator
|
||||
- `src/core/command-generation/adapters/*.ts` - Per-tool adapters
|
||||
- **Backward Compatibility**: Existing workflows unaffected - this is a new command setup feature
|
||||
- **User-Facing**: Required `--tool` flag on `artifact-experimental-setup` command for explicit tool selection
|
||||
@@ -0,0 +1,63 @@
|
||||
# ai-tool-paths Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define the path configuration for AI coding tool skill directories, enabling skill generation to target different tools following the Agent Skills spec.
|
||||
|
||||
## Requirements
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: AIToolOption skillsDir field
|
||||
|
||||
The `AIToolOption` interface SHALL include an optional `skillsDir` field for skill generation path configuration.
|
||||
|
||||
#### Scenario: Interface includes skillsDir field
|
||||
|
||||
- **WHEN** a tool entry is defined in `AI_TOOLS` that supports skill generation
|
||||
- **THEN** it SHALL include a `skillsDir` field specifying the project-local base directory (e.g., `.claude`)
|
||||
|
||||
#### Scenario: Skills path follows Agent Skills spec
|
||||
|
||||
- **WHEN** generating skills for a tool with `skillsDir: '.claude'`
|
||||
- **THEN** skills SHALL be written to `<projectRoot>/<skillsDir>/skills/`
|
||||
- **AND** the `/skills` suffix is appended per Agent Skills specification
|
||||
|
||||
### Requirement: Path configuration for supported tools
|
||||
|
||||
The `AI_TOOLS` array SHALL include `skillsDir` for tools that support the Agent Skills specification.
|
||||
|
||||
#### Scenario: Claude Code paths defined
|
||||
|
||||
- **WHEN** looking up the `claude` tool
|
||||
- **THEN** `skillsDir` SHALL be `.claude`
|
||||
|
||||
#### Scenario: Cursor paths defined
|
||||
|
||||
- **WHEN** looking up the `cursor` tool
|
||||
- **THEN** `skillsDir` SHALL be `.cursor`
|
||||
|
||||
#### Scenario: Windsurf paths defined
|
||||
|
||||
- **WHEN** looking up the `windsurf` tool
|
||||
- **THEN** `skillsDir` SHALL be `.windsurf`
|
||||
|
||||
#### Scenario: Tools without skillsDir
|
||||
|
||||
- **WHEN** a tool has no `skillsDir` defined
|
||||
- **THEN** skill generation SHALL error with message indicating the tool is not supported
|
||||
|
||||
### Requirement: Cross-platform path handling
|
||||
|
||||
The system SHALL handle paths correctly across operating systems.
|
||||
|
||||
#### Scenario: Path construction on Windows
|
||||
|
||||
- **WHEN** constructing skill paths on Windows
|
||||
- **THEN** the system SHALL use `path.join()` for all path construction
|
||||
- **AND** SHALL NOT hardcode forward slashes
|
||||
|
||||
#### Scenario: Path construction on Unix
|
||||
|
||||
- **WHEN** constructing skill paths on macOS or Linux
|
||||
- **THEN** the system SHALL use `path.join()` for consistency
|
||||
@@ -0,0 +1,60 @@
|
||||
# cli-artifact-workflow Delta Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Add `--tool` flag to the `artifact-experimental-setup` command for multi-provider support.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool selection flag
|
||||
|
||||
The `artifact-experimental-setup` command SHALL accept a `--tool <tool-id>` flag to specify the target AI tool.
|
||||
|
||||
#### Scenario: Specify tool via flag
|
||||
|
||||
- **WHEN** user runs `openspec artifact-experimental-setup --tool cursor`
|
||||
- **THEN** skill files are generated in `.cursor/skills/`
|
||||
- **AND** command files are generated using Cursor's frontmatter format
|
||||
|
||||
#### Scenario: Missing tool flag
|
||||
|
||||
- **WHEN** user runs `openspec artifact-experimental-setup` without `--tool`
|
||||
- **THEN** the system displays an error requiring the `--tool` flag
|
||||
- **AND** lists valid tool IDs in the error message
|
||||
|
||||
#### Scenario: Unknown tool ID
|
||||
|
||||
- **WHEN** user runs `openspec artifact-experimental-setup --tool unknown-tool`
|
||||
- **AND** the tool ID is not in `AI_TOOLS`
|
||||
- **THEN** the system displays an error listing valid tool IDs
|
||||
|
||||
#### Scenario: Tool without skillsDir
|
||||
|
||||
- **WHEN** user specifies a tool that has no `skillsDir` configured
|
||||
- **THEN** the system displays an error indicating skill generation is not supported for that tool
|
||||
|
||||
#### Scenario: Tool without command adapter
|
||||
|
||||
- **WHEN** user specifies a tool that has `skillsDir` but no command adapter registered
|
||||
- **THEN** skill files are generated successfully
|
||||
- **AND** command generation is skipped with informational message
|
||||
|
||||
### Requirement: Output messaging
|
||||
|
||||
The setup command SHALL display clear output about what was generated.
|
||||
|
||||
#### Scenario: Show target tool in output
|
||||
|
||||
- **WHEN** setup command runs successfully
|
||||
- **THEN** output includes the target tool name (e.g., "Setting up for Cursor...")
|
||||
|
||||
#### Scenario: Show generated paths
|
||||
|
||||
- **WHEN** setup command completes
|
||||
- **THEN** output lists all generated skill file paths
|
||||
- **AND** lists all generated command file paths (if applicable)
|
||||
|
||||
#### Scenario: Show skipped commands message
|
||||
|
||||
- **WHEN** command generation is skipped due to missing adapter
|
||||
- **THEN** output includes message: "Command generation skipped - no adapter for <tool>"
|
||||
@@ -0,0 +1,98 @@
|
||||
# command-generation Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define a generic command generation system that supports multiple AI tools through a Strategy/Adapter pattern, separating command content from tool-specific formatting.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: CommandContent interface
|
||||
|
||||
The system SHALL define a tool-agnostic `CommandContent` interface for command data.
|
||||
|
||||
#### Scenario: CommandContent structure
|
||||
|
||||
- **WHEN** defining a command to generate
|
||||
- **THEN** `CommandContent` SHALL include:
|
||||
- `id`: string identifier (e.g., 'explore', 'apply')
|
||||
- `name`: human-readable name (e.g., 'OpenSpec Explore')
|
||||
- `description`: brief description of command purpose
|
||||
- `category`: grouping category (e.g., 'OpenSpec')
|
||||
- `tags`: array of tag strings
|
||||
- `body`: the command instruction content
|
||||
|
||||
### 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 relative file path for command
|
||||
- `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/commands/opsx/<id>.md`
|
||||
|
||||
### Requirement: Command generator function
|
||||
|
||||
The system SHALL provide a `generateCommand` function that combines content with adapter.
|
||||
|
||||
#### Scenario: Generate command file
|
||||
|
||||
- **WHEN** calling `generateCommand(content, adapter)`
|
||||
- **THEN** it SHALL return an object with:
|
||||
- `path`: the file path from `adapter.getFilePath(content.id)`
|
||||
- `fileContent`: the formatted content from `adapter.formatFile(content)`
|
||||
|
||||
#### Scenario: Generate multiple commands
|
||||
|
||||
- **WHEN** generating all opsx commands for a tool
|
||||
- **THEN** the system SHALL iterate over command contents and generate each using the tool's adapter
|
||||
|
||||
### Requirement: CommandAdapterRegistry
|
||||
|
||||
The system SHALL provide a registry for looking up tool adapters.
|
||||
|
||||
#### Scenario: Get adapter by tool ID
|
||||
|
||||
- **WHEN** calling `CommandAdapterRegistry.get('cursor')`
|
||||
- **THEN** it SHALL return the Cursor adapter or undefined if not registered
|
||||
|
||||
#### Scenario: Get all adapters
|
||||
|
||||
- **WHEN** calling `CommandAdapterRegistry.getAll()`
|
||||
- **THEN** it SHALL return array of all registered adapters
|
||||
|
||||
#### Scenario: Adapter not found
|
||||
|
||||
- **WHEN** looking up an adapter for unregistered tool
|
||||
- **THEN** `CommandAdapterRegistry.get()` SHALL return undefined
|
||||
- **AND** caller SHALL handle missing adapter appropriately
|
||||
|
||||
### Requirement: Shared command body content
|
||||
|
||||
The body content of commands SHALL be shared across all tools.
|
||||
|
||||
#### Scenario: Same instructions across tools
|
||||
|
||||
- **WHEN** generating the 'explore' command for Claude and Cursor
|
||||
- **THEN** both SHALL use the same `body` content
|
||||
- **AND** only the frontmatter and file path SHALL differ
|
||||
@@ -0,0 +1,55 @@
|
||||
## 1. Extend AIToolOption Interface
|
||||
|
||||
- [x] 1.1 Add `skillsDir?: string` field to `AIToolOption` interface in `src/core/config.ts`
|
||||
|
||||
## 2. Add skillsDir to AI_TOOLS
|
||||
|
||||
- [x] 2.1 Add `skillsDir: '.claude'` to Claude Code tool entry
|
||||
- [x] 2.2 Add `skillsDir: '.cursor'` to Cursor tool entry
|
||||
- [x] 2.3 Add `skillsDir: '.windsurf'` to Windsurf tool entry
|
||||
- [x] 2.4 Add skillsDir for other tools with known Agent Skills spec support (codex, opencode, roocode, kilocode, gemini, factory, github-copilot)
|
||||
|
||||
## 3. Create Command Generation Types
|
||||
|
||||
- [x] 3.1 Create `src/core/command-generation/types.ts` with `CommandContent` interface
|
||||
- [x] 3.2 Add `ToolCommandAdapter` interface to types.ts
|
||||
- [x] 3.3 Export types from module index
|
||||
|
||||
## 4. Implement Tool Command Adapters
|
||||
|
||||
- [x] 4.1 Create `src/core/command-generation/adapters/claude.ts` with Claude frontmatter format
|
||||
- [x] 4.2 Create `src/core/command-generation/adapters/cursor.ts` with Cursor frontmatter format
|
||||
- [x] 4.3 Create `src/core/command-generation/adapters/windsurf.ts` with Windsurf frontmatter format
|
||||
- [x] 4.4 Create base adapter or utility for shared YAML formatting logic (if applicable)
|
||||
|
||||
## 5. Create Command Adapter Registry
|
||||
|
||||
- [x] 5.1 Create `src/core/command-generation/registry.ts` with `CommandAdapterRegistry` class
|
||||
- [x] 5.2 Register Claude, Cursor, Windsurf adapters in static initializer
|
||||
- [x] 5.3 Add `get(toolId)` and `getAll()` methods
|
||||
|
||||
## 6. Create Command Generator
|
||||
|
||||
- [x] 6.1 Create `src/core/command-generation/generator.ts` with `generateCommand()` function
|
||||
- [x] 6.2 Add `generateCommands()` function for batch generation
|
||||
- [x] 6.3 Create module index `src/core/command-generation/index.ts` exporting public API
|
||||
|
||||
## 7. Update artifact-experimental-setup Command
|
||||
|
||||
- [x] 7.1 Add `--tool <tool-id>` option (required) to command in `src/commands/artifact-workflow.ts`
|
||||
- [x] 7.2 Add validation: `--tool` flag is required (error if missing with list of valid tools)
|
||||
- [x] 7.3 Add validation: tool exists in AI_TOOLS
|
||||
- [x] 7.4 Add validation: tool has skillsDir configured
|
||||
- [x] 7.5 Replace hardcoded `.claude` skill paths with `tool.skillsDir`
|
||||
- [x] 7.6 Replace hardcoded command generation with `CommandAdapterRegistry.get()` + `generateCommands()`
|
||||
- [x] 7.7 Handle missing adapter gracefully (skip commands with message)
|
||||
- [x] 7.8 Update output messages to show target tool name and paths
|
||||
|
||||
## 8. Testing
|
||||
|
||||
- [x] 8.1 Add unit tests for `CommandContent` and `ToolCommandAdapter` contracts
|
||||
- [x] 8.2 Add unit tests for Claude adapter (path + frontmatter format)
|
||||
- [x] 8.3 Add unit tests for Cursor adapter (path + frontmatter format)
|
||||
- [x] 8.4 Add unit tests for `CommandAdapterRegistry.get()` and missing adapter case
|
||||
- [x] 8.5 Add integration test for `--tool` flag validation
|
||||
- [x] 8.6 Verify cross-platform path handling uses `path.join()` throughout
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-01-20
|
||||
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
We want to rename `spec-driven` to `openspec-default` to better reflect that it's the standard/default workflow. However, renaming directly would break existing projects that have `schema: spec-driven` in their `openspec/config.yaml`. Adding alias support allows both names to work interchangeably, enabling a smooth transition with no breaking changes.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add schema alias resolution in the schema resolver
|
||||
- `openspec-default` and `spec-driven` will both resolve to the same schema
|
||||
- The physical directory remains `schemas/spec-driven/` (or could be renamed to `schemas/openspec-default/` with `spec-driven` as the alias)
|
||||
- All CLI commands and config files accept either name
|
||||
- No changes required to existing user configs
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `schema-aliases`: Support for schema name aliases so multiple names can resolve to the same schema directory
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
<!-- No existing spec-level behavior is changing - this is purely additive -->
|
||||
|
||||
## Impact
|
||||
|
||||
- `src/core/artifact-graph/resolver.ts` - Add alias resolution logic
|
||||
- `schemas/` directory - Potentially rename `spec-driven` to `openspec-default`
|
||||
- Documentation - Update to prefer `openspec-default` while noting `spec-driven` still works
|
||||
- Default schema constants - Update `DEFAULT_SCHEMA` to `openspec-default`
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fission-ai/openspec",
|
||||
"version": "0.22.0",
|
||||
"version": "1.0.2",
|
||||
"description": "AI-native system for spec-driven development",
|
||||
"keywords": [
|
||||
"openspec",
|
||||
|
||||
Generated
+14
-14
@@ -463,8 +463,8 @@ packages:
|
||||
'@types/node':
|
||||
optional: true
|
||||
|
||||
'@inquirer/type@3.0.8':
|
||||
resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==}
|
||||
'@inquirer/type@3.0.10':
|
||||
resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
'@types/node': '>=18'
|
||||
@@ -1946,7 +1946,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
@@ -1955,7 +1955,7 @@ snapshots:
|
||||
'@inquirer/confirm@5.1.14(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
|
||||
@@ -1963,7 +1963,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@inquirer/ansi': 1.0.0
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
cli-width: 4.1.0
|
||||
mute-stream: 2.0.0
|
||||
signal-exit: 4.1.0
|
||||
@@ -1975,7 +1975,7 @@ snapshots:
|
||||
'@inquirer/editor@4.2.15(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
external-editor: 3.1.0
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
@@ -1983,7 +1983,7 @@ snapshots:
|
||||
'@inquirer/expand@4.0.17(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
@@ -2000,21 +2000,21 @@ snapshots:
|
||||
'@inquirer/input@4.2.1(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
|
||||
'@inquirer/number@3.0.17(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
|
||||
'@inquirer/password@4.0.17(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
ansi-escapes: 4.3.2
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
@@ -2037,7 +2037,7 @@ snapshots:
|
||||
'@inquirer/rawlist@4.1.5(@types/node@24.2.0)':
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
@@ -2046,7 +2046,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
@@ -2055,13 +2055,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@inquirer/core': 10.2.2(@types/node@24.2.0)
|
||||
'@inquirer/figures': 1.0.13
|
||||
'@inquirer/type': 3.0.8(@types/node@24.2.0)
|
||||
'@inquirer/type': 3.0.10(@types/node@24.2.0)
|
||||
ansi-escapes: 4.3.2
|
||||
yoctocolors-cjs: 2.1.2
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
|
||||
'@inquirer/type@3.0.8(@types/node@24.2.0)':
|
||||
'@inquirer/type@3.0.10(@types/node@24.2.0)':
|
||||
optionalDependencies:
|
||||
'@types/node': 24.2.0
|
||||
|
||||
|
||||
@@ -34,7 +34,9 @@ artifacts:
|
||||
instruction: |
|
||||
Create specification files that define WHAT the system should do.
|
||||
|
||||
Create one spec file per capability/feature area in specs/<name>/spec.md.
|
||||
Create one spec file per capability listed in the proposal's Capabilities section.
|
||||
- New capabilities: use the exact kebab-case name from the proposal (specs/<capability>/spec.md).
|
||||
- Modified capabilities: use the existing spec folder name from openspec/specs/<capability>/ when creating the delta spec at specs/<capability>/spec.md.
|
||||
|
||||
Delta operations (use ## headers):
|
||||
- **ADDED Requirements**: New capabilities
|
||||
@@ -110,14 +112,17 @@ artifacts:
|
||||
|
||||
- id: tasks
|
||||
generates: tasks.md
|
||||
description: Implementation tasks derived from specs and design
|
||||
description: Implementation checklist with trackable tasks
|
||||
template: tasks.md
|
||||
instruction: |
|
||||
Create the task list that breaks down the implementation work.
|
||||
|
||||
**IMPORTANT: Follow the template below exactly.** The apply phase parses
|
||||
checkbox format to track progress. Tasks not using `- [ ]` won't be tracked.
|
||||
|
||||
Guidelines:
|
||||
- Group related tasks under ## numbered headings
|
||||
- Each task is a checkbox: - [ ] X.Y Task description
|
||||
- Each task MUST be a checkbox: `- [ ] X.Y Task description`
|
||||
- Tasks should be small enough to complete in one session
|
||||
- Order tasks by dependency (what must be done first?)
|
||||
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
name: tdd
|
||||
version: 1
|
||||
description: Test-driven development workflow - tests → implementation → docs
|
||||
artifacts:
|
||||
- id: spec
|
||||
generates: spec.md
|
||||
description: Feature specification defining requirements
|
||||
template: spec.md
|
||||
instruction: |
|
||||
Create the feature specification that defines WHAT to build.
|
||||
|
||||
Sections:
|
||||
- **Feature**: Name and high-level description of the feature's purpose and user value
|
||||
- **Requirements**: List of specific requirements. Use SHALL/MUST for normative language.
|
||||
- **Acceptance Criteria**: Testable criteria in WHEN/THEN format
|
||||
|
||||
Format requirements:
|
||||
- Each requirement should be specific and testable
|
||||
- Use `#### Scenario: <name>` with WHEN/THEN format for acceptance criteria
|
||||
- Define edge cases and error scenarios explicitly
|
||||
- Every requirement MUST have at least one scenario
|
||||
|
||||
Example:
|
||||
```
|
||||
## Feature: User Authentication
|
||||
|
||||
Users can securely log into the application.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Password validation
|
||||
The system SHALL validate passwords meet minimum security requirements.
|
||||
|
||||
#### Scenario: Valid password accepted
|
||||
- **WHEN** password has 8+ chars, uppercase, lowercase, and number
|
||||
- **THEN** password is accepted
|
||||
|
||||
#### Scenario: Weak password rejected
|
||||
- **WHEN** password is less than 8 characters
|
||||
- **THEN** system displays "Password too short" error
|
||||
```
|
||||
|
||||
This spec drives test creation - each scenario becomes a test case.
|
||||
requires: []
|
||||
|
||||
- id: tests
|
||||
generates: "tests/*.test.ts"
|
||||
description: Test files written before implementation
|
||||
template: test.md
|
||||
instruction: |
|
||||
Write tests BEFORE implementation (TDD red phase).
|
||||
|
||||
File naming:
|
||||
- Create test files as `tests/<feature>.test.ts`
|
||||
- One test file per feature/capability
|
||||
- Use descriptive names matching the spec
|
||||
|
||||
Test structure:
|
||||
- Use Given/When/Then format matching spec scenarios
|
||||
- Group related tests with `describe()` blocks
|
||||
- Each scenario from spec becomes at least one `it()` test
|
||||
|
||||
Coverage requirements:
|
||||
- Cover each requirement from the spec
|
||||
- Include happy path (success cases)
|
||||
- Include edge cases (boundary conditions)
|
||||
- Include error scenarios (invalid input, failures)
|
||||
- Tests should fail initially (no implementation yet)
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
describe('Password validation', () => {
|
||||
it('accepts valid password with all requirements', () => {
|
||||
// GIVEN a password meeting all requirements
|
||||
const password = 'SecurePass1';
|
||||
// WHEN validating
|
||||
const result = validatePassword(password);
|
||||
// THEN it should be accepted
|
||||
expect(result.valid).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects password shorter than 8 characters', () => {
|
||||
// GIVEN a short password
|
||||
const password = 'Short1';
|
||||
// WHEN validating
|
||||
const result = validatePassword(password);
|
||||
// THEN it should be rejected with message
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.error).toBe('Password too short');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Follow the spec requirements exactly - tests verify the spec.
|
||||
requires:
|
||||
- spec
|
||||
|
||||
- id: implementation
|
||||
generates: "src/*.ts"
|
||||
description: Implementation code to pass the tests
|
||||
template: implementation.md
|
||||
instruction: |
|
||||
Implement the feature to make tests pass (TDD green phase).
|
||||
|
||||
TDD workflow:
|
||||
1. Run tests - confirm they fail (red)
|
||||
2. Write minimal code to pass ONE test
|
||||
3. Run tests - confirm that test passes (green)
|
||||
4. Refactor if needed while keeping tests green
|
||||
5. Repeat for next failing test
|
||||
|
||||
Implementation guidelines:
|
||||
- Write minimal code to pass each test - no more, no less
|
||||
- Run tests frequently to verify progress
|
||||
- Keep functions small and focused
|
||||
- Use clear, descriptive names
|
||||
|
||||
Code organization:
|
||||
- Create source files in `src/<feature>.ts`
|
||||
- Export public API clearly
|
||||
- Keep implementation details private
|
||||
- Add JSDoc comments for public functions
|
||||
|
||||
Example structure:
|
||||
```typescript
|
||||
/**
|
||||
* Validates a password meets security requirements.
|
||||
* @param password - The password to validate
|
||||
* @returns Validation result with valid flag and optional error
|
||||
*/
|
||||
export function validatePassword(password: string): ValidationResult {
|
||||
if (password.length < 8) {
|
||||
return { valid: false, error: 'Password too short' };
|
||||
}
|
||||
// ... additional checks
|
||||
return { valid: true };
|
||||
}
|
||||
```
|
||||
|
||||
Don't over-engineer - implement only what tests require.
|
||||
requires:
|
||||
- tests
|
||||
|
||||
- id: docs
|
||||
generates: "docs/*.md"
|
||||
description: Documentation for the implemented feature
|
||||
template: docs.md
|
||||
instruction: |
|
||||
Document the implemented feature.
|
||||
|
||||
Sections:
|
||||
- **Overview**: What the feature does and why it exists (1-2 paragraphs)
|
||||
- **Getting Started**: Quick start guide to use the feature immediately
|
||||
- **Examples**: Code examples showing common use cases
|
||||
- **Reference**: Detailed API documentation, configuration options
|
||||
|
||||
Guidelines:
|
||||
- Write for the user, not the developer
|
||||
- Start with the most common use case
|
||||
- Include copy-pasteable code examples
|
||||
- Document all configuration options with defaults
|
||||
- Note any limitations, edge cases, or gotchas
|
||||
- Link to related features or specs
|
||||
|
||||
Example structure:
|
||||
```markdown
|
||||
## Overview
|
||||
|
||||
Password validation ensures user passwords meet security requirements
|
||||
before account creation or password changes.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Import and use the validation function:
|
||||
|
||||
```typescript
|
||||
import { validatePassword } from './password';
|
||||
|
||||
const result = validatePassword('MySecurePass1');
|
||||
if (!result.valid) {
|
||||
console.error(result.error);
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic validation
|
||||
...
|
||||
|
||||
### Custom error handling
|
||||
...
|
||||
|
||||
## Reference
|
||||
|
||||
### validatePassword(password)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| password | string | The password to validate |
|
||||
|
||||
**Returns**: `{ valid: boolean, error?: string }`
|
||||
```
|
||||
|
||||
Reference the spec for requirements, implementation for details.
|
||||
requires:
|
||||
- implementation
|
||||
|
||||
apply:
|
||||
requires: [tests]
|
||||
tracks: null
|
||||
instruction: |
|
||||
Run tests to see failures. Implement minimal code to pass each test.
|
||||
Refactor while keeping tests green.
|
||||
@@ -1,15 +0,0 @@
|
||||
## Overview
|
||||
|
||||
<!-- Feature overview -->
|
||||
|
||||
## Getting Started
|
||||
|
||||
<!-- Quick start guide -->
|
||||
|
||||
## Examples
|
||||
|
||||
<!-- Code examples -->
|
||||
|
||||
## Reference
|
||||
|
||||
<!-- API reference or additional details -->
|
||||
@@ -1,11 +0,0 @@
|
||||
## Implementation Notes
|
||||
|
||||
<!-- Technical implementation details -->
|
||||
|
||||
## API
|
||||
|
||||
<!-- Public API documentation -->
|
||||
|
||||
## Usage
|
||||
|
||||
<!-- Usage examples -->
|
||||
@@ -1,11 +0,0 @@
|
||||
## Feature: <!-- feature name -->
|
||||
|
||||
<!-- Feature description -->
|
||||
|
||||
## Requirements
|
||||
|
||||
<!-- List of requirements -->
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
<!-- List of acceptance criteria -->
|
||||
@@ -1,11 +0,0 @@
|
||||
## Test Plan
|
||||
|
||||
<!-- Describe the testing strategy -->
|
||||
|
||||
## Test Cases
|
||||
|
||||
### <!-- Test case name -->
|
||||
|
||||
- **Given:** <!-- preconditions -->
|
||||
- **When:** <!-- action -->
|
||||
- **Then:** <!-- expected result -->
|
||||
@@ -41,8 +41,8 @@ sed "${SED_INPLACE[@]}" "s|hash = \"sha256-[^\"]*\"|hash = \"$PLACEHOLDER\"|" "$
|
||||
echo " Building to get correct hash (this will fail)..."
|
||||
BUILD_OUTPUT=$(nix build 2>&1 || true)
|
||||
|
||||
# Extract the correct hash from error output
|
||||
CORRECT_HASH=$(echo "$BUILD_OUTPUT" | grep -oP 'got:\s+\Ksha256-[A-Za-z0-9+/=]+' | head -1)
|
||||
# Extract the correct hash from error output (portable - works on macOS and Linux)
|
||||
CORRECT_HASH=$(echo "$BUILD_OUTPUT" | grep -o 'got:[[:space:]]*sha256-[A-Za-z0-9+/=]*' | head -1 | sed 's/got:[[:space:]]*//')
|
||||
|
||||
if [ -z "$CORRECT_HASH" ]; then
|
||||
echo "❌ Error: Could not extract hash from build output"
|
||||
|
||||
+136
-9
@@ -15,8 +15,21 @@ import { ShowCommand } from '../commands/show.js';
|
||||
import { CompletionCommand } from '../commands/completion.js';
|
||||
import { FeedbackCommand } from '../commands/feedback.js';
|
||||
import { registerConfigCommand } from '../commands/config.js';
|
||||
import { registerArtifactWorkflowCommands } from '../commands/artifact-workflow.js';
|
||||
import { registerSchemaCommand } from '../commands/schema.js';
|
||||
import {
|
||||
statusCommand,
|
||||
instructionsCommand,
|
||||
applyInstructionsCommand,
|
||||
templatesCommand,
|
||||
schemasCommand,
|
||||
newChangeCommand,
|
||||
DEFAULT_SCHEMA,
|
||||
type StatusOptions,
|
||||
type InstructionsOptions,
|
||||
type TemplatesOptions,
|
||||
type SchemasOptions,
|
||||
type NewChangeOptions,
|
||||
} from '../commands/workflow/index.js';
|
||||
import { maybeShowTelemetryNotice, trackCommand, shutdown } from '../telemetry/index.js';
|
||||
|
||||
const program = new Command();
|
||||
@@ -74,18 +87,19 @@ program.hook('postAction', async () => {
|
||||
await shutdown();
|
||||
});
|
||||
|
||||
const availableToolIds = AI_TOOLS.filter((tool) => tool.available).map((tool) => tool.value);
|
||||
const availableToolIds = AI_TOOLS.filter((tool) => tool.skillsDir).map((tool) => tool.value);
|
||||
const toolsOptionDescription = `Configure AI tools non-interactively. Use "all", "none", or a comma-separated list of: ${availableToolIds.join(', ')}`;
|
||||
|
||||
program
|
||||
.command('init [path]')
|
||||
.description('Initialize OpenSpec in your project')
|
||||
.option('--tools <tools>', toolsOptionDescription)
|
||||
.action(async (targetPath = '.', options?: { tools?: string }) => {
|
||||
.option('--force', 'Auto-cleanup legacy files without prompting')
|
||||
.action(async (targetPath = '.', options?: { tools?: string; force?: boolean }) => {
|
||||
try {
|
||||
// Validate that the path is a valid directory
|
||||
const resolvedPath = path.resolve(targetPath);
|
||||
|
||||
|
||||
try {
|
||||
const stats = await fs.stat(resolvedPath);
|
||||
if (!stats.isDirectory()) {
|
||||
@@ -101,10 +115,11 @@ program
|
||||
throw new Error(`Cannot access path "${targetPath}": ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const { InitCommand } = await import('../core/init.js');
|
||||
const initCommand = new InitCommand({
|
||||
tools: options?.tools,
|
||||
force: options?.force,
|
||||
});
|
||||
await initCommand.execute(targetPath);
|
||||
} catch (error) {
|
||||
@@ -114,13 +129,36 @@ program
|
||||
}
|
||||
});
|
||||
|
||||
// Hidden alias: 'experimental' -> 'init' for backwards compatibility
|
||||
program
|
||||
.command('experimental', { hidden: true })
|
||||
.description('Alias for init (deprecated)')
|
||||
.option('--tool <tool-id>', 'Target AI tool (maps to --tools)')
|
||||
.option('--no-interactive', 'Disable interactive prompts')
|
||||
.action(async (options?: { tool?: string; noInteractive?: boolean }) => {
|
||||
try {
|
||||
console.log('Note: "openspec experimental" is deprecated. Use "openspec init" instead.');
|
||||
const { InitCommand } = await import('../core/init.js');
|
||||
const initCommand = new InitCommand({
|
||||
tools: options?.tool,
|
||||
interactive: options?.noInteractive === true ? false : undefined,
|
||||
});
|
||||
await initCommand.execute('.');
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('update [path]')
|
||||
.description('Update OpenSpec instruction files')
|
||||
.action(async (targetPath = '.') => {
|
||||
.option('--force', 'Force update even when tools are up to date')
|
||||
.action(async (targetPath = '.', options?: { force?: boolean }) => {
|
||||
try {
|
||||
const resolvedPath = path.resolve(targetPath);
|
||||
const updateCommand = new UpdateCommand();
|
||||
const updateCommand = new UpdateCommand({ force: options?.force });
|
||||
await updateCommand.execute(resolvedPath);
|
||||
} catch (error) {
|
||||
console.log(); // Empty line for spacing
|
||||
@@ -375,7 +413,96 @@ program
|
||||
}
|
||||
});
|
||||
|
||||
// Register artifact workflow commands (experimental)
|
||||
registerArtifactWorkflowCommands(program);
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Workflow Commands (formerly experimental)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
// Status command
|
||||
program
|
||||
.command('status')
|
||||
.description('Display artifact completion status for a change')
|
||||
.option('--change <id>', 'Change name to show status for')
|
||||
.option('--schema <name>', 'Schema override (auto-detected from config.yaml)')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options: StatusOptions) => {
|
||||
try {
|
||||
await statusCommand(options);
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Instructions command
|
||||
program
|
||||
.command('instructions [artifact]')
|
||||
.description('Output enriched instructions for creating an artifact or applying tasks')
|
||||
.option('--change <id>', 'Change name')
|
||||
.option('--schema <name>', 'Schema override (auto-detected from config.yaml)')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (artifactId: string | undefined, options: InstructionsOptions) => {
|
||||
try {
|
||||
// Special case: "apply" is not an artifact, but a command to get apply instructions
|
||||
if (artifactId === 'apply') {
|
||||
await applyInstructionsCommand(options);
|
||||
} else {
|
||||
await instructionsCommand(artifactId, options);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Templates command
|
||||
program
|
||||
.command('templates')
|
||||
.description('Show resolved template paths for all artifacts in a schema')
|
||||
.option('--schema <name>', `Schema to use (default: ${DEFAULT_SCHEMA})`)
|
||||
.option('--json', 'Output as JSON mapping artifact IDs to template paths')
|
||||
.action(async (options: TemplatesOptions) => {
|
||||
try {
|
||||
await templatesCommand(options);
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Schemas command
|
||||
program
|
||||
.command('schemas')
|
||||
.description('List available workflow schemas with descriptions')
|
||||
.option('--json', 'Output as JSON (for agent use)')
|
||||
.action(async (options: SchemasOptions) => {
|
||||
try {
|
||||
await schemasCommand(options);
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// New command group with change subcommand
|
||||
const newCmd = program.command('new').description('Create new items');
|
||||
|
||||
newCmd
|
||||
.command('change <name>')
|
||||
.description('Create a new change directory')
|
||||
.option('--description <text>', 'Description to add to README.md')
|
||||
.option('--schema <name>', `Workflow schema to use (default: ${DEFAULT_SCHEMA})`)
|
||||
.action(async (name: string, options: NewChangeOptions) => {
|
||||
try {
|
||||
await newChangeCommand(name, options);
|
||||
} catch (error) {
|
||||
console.log();
|
||||
ora().fail(`Error: ${(error as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Workflow CLI Commands
|
||||
*
|
||||
* Commands for the artifact-driven workflow: status, instructions, templates, schemas, new change.
|
||||
*/
|
||||
|
||||
export { statusCommand } from './status.js';
|
||||
export type { StatusOptions } from './status.js';
|
||||
|
||||
export { instructionsCommand, applyInstructionsCommand } from './instructions.js';
|
||||
export type { InstructionsOptions } from './instructions.js';
|
||||
|
||||
export { templatesCommand } from './templates.js';
|
||||
export type { TemplatesOptions } from './templates.js';
|
||||
|
||||
export { schemasCommand } from './schemas.js';
|
||||
export type { SchemasOptions } from './schemas.js';
|
||||
|
||||
export { newChangeCommand } from './new-change.js';
|
||||
export type { NewChangeOptions } from './new-change.js';
|
||||
|
||||
export { DEFAULT_SCHEMA } from './shared.js';
|
||||
@@ -0,0 +1,481 @@
|
||||
/**
|
||||
* Instructions Command
|
||||
*
|
||||
* Generates enriched instructions for creating artifacts or applying tasks.
|
||||
* Includes both artifact instructions and apply instructions.
|
||||
*/
|
||||
|
||||
import ora from 'ora';
|
||||
import path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {
|
||||
loadChangeContext,
|
||||
generateInstructions,
|
||||
resolveSchema,
|
||||
type ArtifactInstructions,
|
||||
} from '../../core/artifact-graph/index.js';
|
||||
import {
|
||||
validateChangeExists,
|
||||
validateSchemaExists,
|
||||
type TaskItem,
|
||||
type ApplyInstructions,
|
||||
} from './shared.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface InstructionsOptions {
|
||||
change?: string;
|
||||
schema?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
export interface ApplyInstructionsOptions {
|
||||
change?: string;
|
||||
schema?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Artifact Instructions Command
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function instructionsCommand(
|
||||
artifactId: string | undefined,
|
||||
options: InstructionsOptions
|
||||
): Promise<void> {
|
||||
const spinner = ora('Generating instructions...').start();
|
||||
|
||||
try {
|
||||
const projectRoot = process.cwd();
|
||||
const changeName = await validateChangeExists(options.change, projectRoot);
|
||||
|
||||
// Validate schema if explicitly provided
|
||||
if (options.schema) {
|
||||
validateSchemaExists(options.schema, projectRoot);
|
||||
}
|
||||
|
||||
// loadChangeContext will auto-detect schema from metadata if not provided
|
||||
const context = loadChangeContext(projectRoot, changeName, options.schema);
|
||||
|
||||
if (!artifactId) {
|
||||
spinner.stop();
|
||||
const validIds = context.graph.getAllArtifacts().map((a) => a.id);
|
||||
throw new Error(
|
||||
`Missing required argument <artifact>. Valid artifacts:\n ${validIds.join('\n ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const artifact = context.graph.getArtifact(artifactId);
|
||||
|
||||
if (!artifact) {
|
||||
spinner.stop();
|
||||
const validIds = context.graph.getAllArtifacts().map((a) => a.id);
|
||||
throw new Error(
|
||||
`Artifact '${artifactId}' not found in schema '${context.schemaName}'. Valid artifacts:\n ${validIds.join('\n ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const instructions = generateInstructions(context, artifactId, projectRoot);
|
||||
const isBlocked = instructions.dependencies.some((d) => !d.done);
|
||||
|
||||
spinner.stop();
|
||||
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(instructions, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
printInstructionsText(instructions, isBlocked);
|
||||
} catch (error) {
|
||||
spinner.stop();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function printInstructionsText(instructions: ArtifactInstructions, isBlocked: boolean): void {
|
||||
const {
|
||||
artifactId,
|
||||
changeName,
|
||||
schemaName,
|
||||
changeDir,
|
||||
outputPath,
|
||||
description,
|
||||
instruction,
|
||||
context,
|
||||
rules,
|
||||
template,
|
||||
dependencies,
|
||||
unlocks,
|
||||
} = instructions;
|
||||
|
||||
// Opening tag
|
||||
console.log(`<artifact id="${artifactId}" change="${changeName}" schema="${schemaName}">`);
|
||||
console.log();
|
||||
|
||||
// Warning for blocked artifacts
|
||||
if (isBlocked) {
|
||||
const missing = dependencies.filter((d) => !d.done).map((d) => d.id);
|
||||
console.log('<warning>');
|
||||
console.log('This artifact has unmet dependencies. Complete them first or proceed with caution.');
|
||||
console.log(`Missing: ${missing.join(', ')}`);
|
||||
console.log('</warning>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Task directive
|
||||
console.log('<task>');
|
||||
console.log(`Create the ${artifactId} artifact for change "${changeName}".`);
|
||||
console.log(description);
|
||||
console.log('</task>');
|
||||
console.log();
|
||||
|
||||
// Project context (AI constraint - do not include in output)
|
||||
if (context) {
|
||||
console.log('<project_context>');
|
||||
console.log('<!-- This is background information for you. Do NOT include this in your output. -->');
|
||||
console.log(context);
|
||||
console.log('</project_context>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Rules (AI constraint - do not include in output)
|
||||
if (rules && rules.length > 0) {
|
||||
console.log('<rules>');
|
||||
console.log('<!-- These are constraints for you to follow. Do NOT include this in your output. -->');
|
||||
for (const rule of rules) {
|
||||
console.log(`- ${rule}`);
|
||||
}
|
||||
console.log('</rules>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Dependencies (files to read for context)
|
||||
if (dependencies.length > 0) {
|
||||
console.log('<dependencies>');
|
||||
console.log('Read these files for context before creating this artifact:');
|
||||
console.log();
|
||||
for (const dep of dependencies) {
|
||||
const status = dep.done ? 'done' : 'missing';
|
||||
const fullPath = path.join(changeDir, dep.path);
|
||||
console.log(`<dependency id="${dep.id}" status="${status}">`);
|
||||
console.log(` <path>${fullPath}</path>`);
|
||||
console.log(` <description>${dep.description}</description>`);
|
||||
console.log('</dependency>');
|
||||
}
|
||||
console.log('</dependencies>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Output location
|
||||
console.log('<output>');
|
||||
console.log(`Write to: ${path.join(changeDir, outputPath)}`);
|
||||
console.log('</output>');
|
||||
console.log();
|
||||
|
||||
// Instruction (guidance)
|
||||
if (instruction) {
|
||||
console.log('<instruction>');
|
||||
console.log(instruction.trim());
|
||||
console.log('</instruction>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Template
|
||||
console.log('<template>');
|
||||
console.log('<!-- Use this as the structure for your output file. Fill in the sections. -->');
|
||||
console.log(template.trim());
|
||||
console.log('</template>');
|
||||
console.log();
|
||||
|
||||
// Success criteria placeholder
|
||||
console.log('<success_criteria>');
|
||||
console.log('<!-- To be defined in schema validation rules -->');
|
||||
console.log('</success_criteria>');
|
||||
console.log();
|
||||
|
||||
// Unlocks
|
||||
if (unlocks.length > 0) {
|
||||
console.log('<unlocks>');
|
||||
console.log(`Completing this artifact enables: ${unlocks.join(', ')}`);
|
||||
console.log('</unlocks>');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Closing tag
|
||||
console.log('</artifact>');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Apply Instructions Command
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses tasks.md content and extracts task items with their completion status.
|
||||
*/
|
||||
function parseTasksFile(content: string): TaskItem[] {
|
||||
const tasks: TaskItem[] = [];
|
||||
const lines = content.split('\n');
|
||||
let taskIndex = 0;
|
||||
|
||||
for (const line of lines) {
|
||||
// Match checkbox patterns: - [ ] or - [x] or - [X]
|
||||
const checkboxMatch = line.match(/^[-*]\s*\[([ xX])\]\s*(.+)\s*$/);
|
||||
if (checkboxMatch) {
|
||||
taskIndex++;
|
||||
const done = checkboxMatch[1].toLowerCase() === 'x';
|
||||
const description = checkboxMatch[2].trim();
|
||||
tasks.push({
|
||||
id: `${taskIndex}`,
|
||||
description,
|
||||
done,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an artifact output exists in the change directory.
|
||||
* Supports glob patterns (e.g., "specs/*.md") by verifying at least one matching file exists.
|
||||
*/
|
||||
function artifactOutputExists(changeDir: string, generates: string): boolean {
|
||||
// Normalize the generates path to use platform-specific separators
|
||||
const normalizedGenerates = generates.split('/').join(path.sep);
|
||||
const fullPath = path.join(changeDir, normalizedGenerates);
|
||||
|
||||
// If it's a glob pattern (contains ** or *), check for matching files
|
||||
if (generates.includes('*')) {
|
||||
// Extract the directory part before the glob pattern
|
||||
const parts = normalizedGenerates.split(path.sep);
|
||||
const dirParts: string[] = [];
|
||||
let patternPart = '';
|
||||
for (const part of parts) {
|
||||
if (part.includes('*')) {
|
||||
patternPart = part;
|
||||
break;
|
||||
}
|
||||
dirParts.push(part);
|
||||
}
|
||||
const dirPath = path.join(changeDir, ...dirParts);
|
||||
|
||||
// Check if directory exists
|
||||
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract expected extension from pattern (e.g., "*.md" -> ".md")
|
||||
const extMatch = patternPart.match(/\*(\.[a-zA-Z0-9]+)$/);
|
||||
const expectedExt = extMatch ? extMatch[1] : null;
|
||||
|
||||
// Recursively check for matching files
|
||||
const hasMatchingFiles = (dir: string): boolean => {
|
||||
try {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
// For ** patterns, recurse into subdirectories
|
||||
if (generates.includes('**') && hasMatchingFiles(path.join(dir, entry.name))) {
|
||||
return true;
|
||||
}
|
||||
} else if (entry.isFile()) {
|
||||
// Check if file matches expected extension (or any file if no extension specified)
|
||||
if (!expectedExt || entry.name.endsWith(expectedExt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return hasMatchingFiles(dirPath);
|
||||
}
|
||||
|
||||
return fs.existsSync(fullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates apply instructions for implementing tasks from a change.
|
||||
* Schema-aware: reads apply phase configuration from schema to determine
|
||||
* required artifacts, tracking file, and instruction.
|
||||
*/
|
||||
export async function generateApplyInstructions(
|
||||
projectRoot: string,
|
||||
changeName: string,
|
||||
schemaName?: string
|
||||
): Promise<ApplyInstructions> {
|
||||
// loadChangeContext will auto-detect schema from metadata if not provided
|
||||
const context = loadChangeContext(projectRoot, changeName, schemaName);
|
||||
const changeDir = path.join(projectRoot, 'openspec', 'changes', changeName);
|
||||
|
||||
// Get the full schema to access the apply phase configuration
|
||||
const schema = resolveSchema(context.schemaName, projectRoot);
|
||||
const applyConfig = schema.apply;
|
||||
|
||||
// Determine required artifacts and tracking file from schema
|
||||
// Fallback: if no apply block, require all artifacts
|
||||
const requiredArtifactIds = applyConfig?.requires ?? schema.artifacts.map((a) => a.id);
|
||||
const tracksFile = applyConfig?.tracks ?? null;
|
||||
const schemaInstruction = applyConfig?.instruction ?? null;
|
||||
|
||||
// Check which required artifacts are missing
|
||||
const missingArtifacts: string[] = [];
|
||||
for (const artifactId of requiredArtifactIds) {
|
||||
const artifact = schema.artifacts.find((a) => a.id === artifactId);
|
||||
if (artifact && !artifactOutputExists(changeDir, artifact.generates)) {
|
||||
missingArtifacts.push(artifactId);
|
||||
}
|
||||
}
|
||||
|
||||
// Build context files from all existing artifacts in schema
|
||||
const contextFiles: Record<string, string> = {};
|
||||
for (const artifact of schema.artifacts) {
|
||||
if (artifactOutputExists(changeDir, artifact.generates)) {
|
||||
contextFiles[artifact.id] = path.join(changeDir, artifact.generates);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse tasks if tracking file exists
|
||||
let tasks: TaskItem[] = [];
|
||||
let tracksFileExists = false;
|
||||
if (tracksFile) {
|
||||
const tracksPath = path.join(changeDir, tracksFile);
|
||||
tracksFileExists = fs.existsSync(tracksPath);
|
||||
if (tracksFileExists) {
|
||||
const tasksContent = await fs.promises.readFile(tracksPath, 'utf-8');
|
||||
tasks = parseTasksFile(tasksContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate progress
|
||||
const total = tasks.length;
|
||||
const complete = tasks.filter((t) => t.done).length;
|
||||
const remaining = total - complete;
|
||||
|
||||
// Determine state and instruction
|
||||
let state: ApplyInstructions['state'];
|
||||
let instruction: string;
|
||||
|
||||
if (missingArtifacts.length > 0) {
|
||||
state = 'blocked';
|
||||
instruction = `Cannot apply this change yet. Missing artifacts: ${missingArtifacts.join(', ')}.\nUse the openspec-continue-change skill to create the missing artifacts first.`;
|
||||
} else if (tracksFile && !tracksFileExists) {
|
||||
// Tracking file configured but doesn't exist yet
|
||||
const tracksFilename = path.basename(tracksFile);
|
||||
state = 'blocked';
|
||||
instruction = `The ${tracksFilename} file is missing and must be created.\nUse openspec-continue-change to generate the tracking file.`;
|
||||
} else if (tracksFile && tracksFileExists && total === 0) {
|
||||
// Tracking file exists but contains no tasks
|
||||
const tracksFilename = path.basename(tracksFile);
|
||||
state = 'blocked';
|
||||
instruction = `The ${tracksFilename} file exists but contains no tasks.\nAdd tasks to ${tracksFilename} or regenerate it with openspec-continue-change.`;
|
||||
} else if (tracksFile && remaining === 0 && total > 0) {
|
||||
state = 'all_done';
|
||||
instruction = 'All tasks are complete! This change is ready to be archived.\nConsider running tests and reviewing the changes before archiving.';
|
||||
} else if (!tracksFile) {
|
||||
// No tracking file configured in schema - ready to apply
|
||||
state = 'ready';
|
||||
instruction = schemaInstruction?.trim() ?? 'All required artifacts complete. Proceed with implementation.';
|
||||
} else {
|
||||
state = 'ready';
|
||||
instruction = schemaInstruction?.trim() ?? 'Read context files, work through pending tasks, mark complete as you go.\nPause if you hit blockers or need clarification.';
|
||||
}
|
||||
|
||||
return {
|
||||
changeName,
|
||||
changeDir,
|
||||
schemaName: context.schemaName,
|
||||
contextFiles,
|
||||
progress: { total, complete, remaining },
|
||||
tasks,
|
||||
state,
|
||||
missingArtifacts: missingArtifacts.length > 0 ? missingArtifacts : undefined,
|
||||
instruction,
|
||||
};
|
||||
}
|
||||
|
||||
export async function applyInstructionsCommand(options: ApplyInstructionsOptions): Promise<void> {
|
||||
const spinner = ora('Generating apply instructions...').start();
|
||||
|
||||
try {
|
||||
const projectRoot = process.cwd();
|
||||
const changeName = await validateChangeExists(options.change, projectRoot);
|
||||
|
||||
// Validate schema if explicitly provided
|
||||
if (options.schema) {
|
||||
validateSchemaExists(options.schema, projectRoot);
|
||||
}
|
||||
|
||||
// generateApplyInstructions uses loadChangeContext which auto-detects schema
|
||||
const instructions = await generateApplyInstructions(projectRoot, changeName, options.schema);
|
||||
|
||||
spinner.stop();
|
||||
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(instructions, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
printApplyInstructionsText(instructions);
|
||||
} catch (error) {
|
||||
spinner.stop();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function printApplyInstructionsText(instructions: ApplyInstructions): void {
|
||||
const { changeName, schemaName, contextFiles, progress, tasks, state, missingArtifacts, instruction } = instructions;
|
||||
|
||||
console.log(`## Apply: ${changeName}`);
|
||||
console.log(`Schema: ${schemaName}`);
|
||||
console.log();
|
||||
|
||||
// Warning for blocked state
|
||||
if (state === 'blocked' && missingArtifacts) {
|
||||
console.log('### ⚠️ Blocked');
|
||||
console.log();
|
||||
console.log(`Missing artifacts: ${missingArtifacts.join(', ')}`);
|
||||
console.log('Use the openspec-continue-change skill to create these first.');
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Context files (dynamically from schema)
|
||||
const contextFileEntries = Object.entries(contextFiles);
|
||||
if (contextFileEntries.length > 0) {
|
||||
console.log('### Context Files');
|
||||
for (const [artifactId, filePath] of contextFileEntries) {
|
||||
console.log(`- ${artifactId}: ${filePath}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Progress (only show if we have tracking)
|
||||
if (progress.total > 0 || tasks.length > 0) {
|
||||
console.log('### Progress');
|
||||
if (state === 'all_done') {
|
||||
console.log(`${progress.complete}/${progress.total} complete ✓`);
|
||||
} else {
|
||||
console.log(`${progress.complete}/${progress.total} complete`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Tasks
|
||||
if (tasks.length > 0) {
|
||||
console.log('### Tasks');
|
||||
for (const task of tasks) {
|
||||
const checkbox = task.done ? '[x]' : '[ ]';
|
||||
console.log(`- ${checkbox} ${task.description}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
// Instruction
|
||||
console.log('### Instruction');
|
||||
console.log(instruction);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* New Change Command
|
||||
*
|
||||
* Creates a new change directory with optional description and schema.
|
||||
*/
|
||||
|
||||
import ora from 'ora';
|
||||
import path from 'path';
|
||||
import { createChange, validateChangeName } from '../../utils/change-utils.js';
|
||||
import { validateSchemaExists } from './shared.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface NewChangeOptions {
|
||||
description?: string;
|
||||
schema?: string;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Command Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function newChangeCommand(name: string | undefined, options: NewChangeOptions): Promise<void> {
|
||||
if (!name) {
|
||||
throw new Error('Missing required argument <name>');
|
||||
}
|
||||
|
||||
const validation = validateChangeName(name);
|
||||
if (!validation.valid) {
|
||||
throw new Error(validation.error);
|
||||
}
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
|
||||
// Validate schema if provided
|
||||
if (options.schema) {
|
||||
validateSchemaExists(options.schema, projectRoot);
|
||||
}
|
||||
|
||||
const schemaDisplay = options.schema ? ` with schema '${options.schema}'` : '';
|
||||
const spinner = ora(`Creating change '${name}'${schemaDisplay}...`).start();
|
||||
|
||||
try {
|
||||
const result = await createChange(projectRoot, name, { schema: options.schema });
|
||||
|
||||
// If description provided, create README.md with description
|
||||
if (options.description) {
|
||||
const { promises: fs } = await import('fs');
|
||||
const changeDir = path.join(projectRoot, 'openspec', 'changes', name);
|
||||
const readmePath = path.join(changeDir, 'README.md');
|
||||
await fs.writeFile(readmePath, `# ${name}\n\n${options.description}\n`, 'utf-8');
|
||||
}
|
||||
|
||||
spinner.succeed(`Created change '${name}' at openspec/changes/${name}/ (schema: ${result.schema})`);
|
||||
} catch (error) {
|
||||
spinner.fail(`Failed to create change '${name}'`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Schemas Command
|
||||
*
|
||||
* Lists available workflow schemas with descriptions.
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { listSchemasWithInfo } from '../../core/artifact-graph/index.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface SchemasOptions {
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Command Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function schemasCommand(options: SchemasOptions): Promise<void> {
|
||||
const projectRoot = process.cwd();
|
||||
const schemas = listSchemasWithInfo(projectRoot);
|
||||
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(schemas, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Available schemas:');
|
||||
console.log();
|
||||
|
||||
for (const schema of schemas) {
|
||||
let sourceLabel = '';
|
||||
if (schema.source === 'project') {
|
||||
sourceLabel = chalk.cyan(' (project)');
|
||||
} else if (schema.source === 'user') {
|
||||
sourceLabel = chalk.dim(' (user override)');
|
||||
}
|
||||
console.log(` ${chalk.bold(schema.name)}${sourceLabel}`);
|
||||
console.log(` ${schema.description}`);
|
||||
console.log(` Artifacts: ${schema.artifacts.join(' → ')}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Shared Types and Utilities for Artifact Workflow Commands
|
||||
*
|
||||
* This module contains types, constants, and validation helpers used across
|
||||
* multiple artifact workflow commands.
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
import path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { getSchemaDir, listSchemas } from '../../core/artifact-graph/index.js';
|
||||
import { validateChangeName } from '../../utils/change-utils.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface TaskItem {
|
||||
id: string;
|
||||
description: string;
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
export interface ApplyInstructions {
|
||||
changeName: string;
|
||||
changeDir: string;
|
||||
schemaName: string;
|
||||
contextFiles: Record<string, string>;
|
||||
progress: {
|
||||
total: number;
|
||||
complete: number;
|
||||
remaining: number;
|
||||
};
|
||||
tasks: TaskItem[];
|
||||
state: 'blocked' | 'all_done' | 'ready';
|
||||
missingArtifacts?: string[];
|
||||
instruction: string;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constants
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export const DEFAULT_SCHEMA = 'spec-driven';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Utility Functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks if color output is disabled via NO_COLOR env or --no-color flag.
|
||||
*/
|
||||
export function isColorDisabled(): boolean {
|
||||
return process.env.NO_COLOR === '1' || process.env.NO_COLOR === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color function based on status.
|
||||
*/
|
||||
export function getStatusColor(status: 'done' | 'ready' | 'blocked'): (text: string) => string {
|
||||
if (isColorDisabled()) {
|
||||
return (text: string) => text;
|
||||
}
|
||||
switch (status) {
|
||||
case 'done':
|
||||
return chalk.green;
|
||||
case 'ready':
|
||||
return chalk.yellow;
|
||||
case 'blocked':
|
||||
return chalk.red;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status indicator for an artifact.
|
||||
*/
|
||||
export function getStatusIndicator(status: 'done' | 'ready' | 'blocked'): string {
|
||||
const color = getStatusColor(status);
|
||||
switch (status) {
|
||||
case 'done':
|
||||
return color('[x]');
|
||||
case 'ready':
|
||||
return color('[ ]');
|
||||
case 'blocked':
|
||||
return color('[-]');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a change exists and returns available changes if not.
|
||||
* Checks directory existence directly to support scaffolded changes (without proposal.md).
|
||||
*/
|
||||
export async function validateChangeExists(
|
||||
changeName: string | undefined,
|
||||
projectRoot: string
|
||||
): Promise<string> {
|
||||
const changesPath = path.join(projectRoot, 'openspec', 'changes');
|
||||
|
||||
// Get all change directories (not just those with proposal.md)
|
||||
const getAvailableChanges = async (): Promise<string[]> => {
|
||||
try {
|
||||
const entries = await fs.promises.readdir(changesPath, { withFileTypes: true });
|
||||
return entries
|
||||
.filter((e) => e.isDirectory() && e.name !== 'archive' && !e.name.startsWith('.'))
|
||||
.map((e) => e.name);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
if (!changeName) {
|
||||
const available = await getAvailableChanges();
|
||||
if (available.length === 0) {
|
||||
throw new Error('No changes found. Create one with: openspec new change <name>');
|
||||
}
|
||||
throw new Error(
|
||||
`Missing required option --change. Available changes:\n ${available.join('\n ')}`
|
||||
);
|
||||
}
|
||||
|
||||
// Validate change name format to prevent path traversal
|
||||
const nameValidation = validateChangeName(changeName);
|
||||
if (!nameValidation.valid) {
|
||||
throw new Error(`Invalid change name '${changeName}': ${nameValidation.error}`);
|
||||
}
|
||||
|
||||
// Check directory existence directly
|
||||
const changePath = path.join(changesPath, changeName);
|
||||
const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();
|
||||
|
||||
if (!exists) {
|
||||
const available = await getAvailableChanges();
|
||||
if (available.length === 0) {
|
||||
throw new Error(
|
||||
`Change '${changeName}' not found. No changes exist. Create one with: openspec new change <name>`
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Change '${changeName}' not found. Available changes:\n ${available.join('\n ')}`
|
||||
);
|
||||
}
|
||||
|
||||
return changeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a schema exists and returns available schemas if not.
|
||||
*
|
||||
* @param schemaName - The schema name to validate
|
||||
* @param projectRoot - Optional project root for project-local schema resolution
|
||||
*/
|
||||
export function validateSchemaExists(schemaName: string, projectRoot?: string): string {
|
||||
const schemaDir = getSchemaDir(schemaName, projectRoot);
|
||||
if (!schemaDir) {
|
||||
const availableSchemas = listSchemas(projectRoot);
|
||||
throw new Error(
|
||||
`Schema '${schemaName}' not found. Available schemas:\n ${availableSchemas.join('\n ')}`
|
||||
);
|
||||
}
|
||||
return schemaName;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Status Command
|
||||
*
|
||||
* Displays artifact completion status for a change.
|
||||
*/
|
||||
|
||||
import ora from 'ora';
|
||||
import chalk from 'chalk';
|
||||
import {
|
||||
loadChangeContext,
|
||||
formatChangeStatus,
|
||||
type ChangeStatus,
|
||||
} from '../../core/artifact-graph/index.js';
|
||||
import {
|
||||
validateChangeExists,
|
||||
validateSchemaExists,
|
||||
getStatusIndicator,
|
||||
getStatusColor,
|
||||
} from './shared.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface StatusOptions {
|
||||
change?: string;
|
||||
schema?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Command Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function statusCommand(options: StatusOptions): Promise<void> {
|
||||
const spinner = ora('Loading change status...').start();
|
||||
|
||||
try {
|
||||
const projectRoot = process.cwd();
|
||||
const changeName = await validateChangeExists(options.change, projectRoot);
|
||||
|
||||
// Validate schema if explicitly provided
|
||||
if (options.schema) {
|
||||
validateSchemaExists(options.schema, projectRoot);
|
||||
}
|
||||
|
||||
// loadChangeContext will auto-detect schema from metadata if not provided
|
||||
const context = loadChangeContext(projectRoot, changeName, options.schema);
|
||||
const status = formatChangeStatus(context);
|
||||
|
||||
spinner.stop();
|
||||
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(status, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
printStatusText(status);
|
||||
} catch (error) {
|
||||
spinner.stop();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function printStatusText(status: ChangeStatus): void {
|
||||
const doneCount = status.artifacts.filter((a) => a.status === 'done').length;
|
||||
const total = status.artifacts.length;
|
||||
|
||||
console.log(`Change: ${status.changeName}`);
|
||||
console.log(`Schema: ${status.schemaName}`);
|
||||
console.log(`Progress: ${doneCount}/${total} artifacts complete`);
|
||||
console.log();
|
||||
|
||||
for (const artifact of status.artifacts) {
|
||||
const indicator = getStatusIndicator(artifact.status);
|
||||
const color = getStatusColor(artifact.status);
|
||||
let line = `${indicator} ${artifact.id}`;
|
||||
|
||||
if (artifact.status === 'blocked' && artifact.missingDeps && artifact.missingDeps.length > 0) {
|
||||
line += color(` (blocked by: ${artifact.missingDeps.join(', ')})`);
|
||||
}
|
||||
|
||||
console.log(line);
|
||||
}
|
||||
|
||||
if (status.isComplete) {
|
||||
console.log();
|
||||
console.log(chalk.green('All artifacts complete!'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Templates Command
|
||||
*
|
||||
* Shows resolved template paths for all artifacts in a schema.
|
||||
*/
|
||||
|
||||
import ora from 'ora';
|
||||
import path from 'path';
|
||||
import {
|
||||
resolveSchema,
|
||||
getSchemaDir,
|
||||
ArtifactGraph,
|
||||
} from '../../core/artifact-graph/index.js';
|
||||
import { validateSchemaExists, DEFAULT_SCHEMA } from './shared.js';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export interface TemplatesOptions {
|
||||
schema?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
export interface TemplateInfo {
|
||||
artifactId: string;
|
||||
templatePath: string;
|
||||
source: 'project' | 'user' | 'package';
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Command Implementation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export async function templatesCommand(options: TemplatesOptions): Promise<void> {
|
||||
const spinner = ora('Loading templates...').start();
|
||||
|
||||
try {
|
||||
const projectRoot = process.cwd();
|
||||
const schemaName = validateSchemaExists(options.schema ?? DEFAULT_SCHEMA, projectRoot);
|
||||
const schema = resolveSchema(schemaName, projectRoot);
|
||||
const graph = ArtifactGraph.fromSchema(schema);
|
||||
const schemaDir = getSchemaDir(schemaName, projectRoot)!;
|
||||
|
||||
// Determine the source (project, user, or package)
|
||||
const {
|
||||
getUserSchemasDir,
|
||||
getProjectSchemasDir,
|
||||
} = await import('../../core/artifact-graph/resolver.js');
|
||||
const projectSchemasDir = getProjectSchemasDir(projectRoot);
|
||||
const userSchemasDir = getUserSchemasDir();
|
||||
|
||||
// Determine source by checking if schemaDir is inside each base directory
|
||||
// Using path.relative is more robust than startsWith for path comparisons
|
||||
const isInsideDir = (child: string, parent: string): boolean => {
|
||||
const relative = path.relative(parent, child);
|
||||
return !relative.startsWith('..') && !path.isAbsolute(relative);
|
||||
};
|
||||
|
||||
let source: 'project' | 'user' | 'package';
|
||||
if (isInsideDir(schemaDir, projectSchemasDir)) {
|
||||
source = 'project';
|
||||
} else if (isInsideDir(schemaDir, userSchemasDir)) {
|
||||
source = 'user';
|
||||
} else {
|
||||
source = 'package';
|
||||
}
|
||||
|
||||
const templates: TemplateInfo[] = graph.getAllArtifacts().map((artifact) => ({
|
||||
artifactId: artifact.id,
|
||||
templatePath: path.join(schemaDir, 'templates', artifact.template),
|
||||
source,
|
||||
}));
|
||||
|
||||
spinner.stop();
|
||||
|
||||
if (options.json) {
|
||||
const output: Record<string, { path: string; source: string }> = {};
|
||||
for (const t of templates) {
|
||||
output[t.artifactId] = { path: t.templatePath, source: t.source };
|
||||
}
|
||||
console.log(JSON.stringify(output, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Schema: ${schemaName}`);
|
||||
console.log(`Source: ${source}`);
|
||||
console.log();
|
||||
|
||||
for (const t of templates) {
|
||||
console.log(`${t.artifactId}:`);
|
||||
console.log(` ${t.templatePath}`);
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.stop();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,11 @@ export interface ArtifactInstructions {
|
||||
description: string;
|
||||
/** Guidance on how to create this artifact (from schema instruction field) */
|
||||
instruction: string | undefined;
|
||||
/** Template content (structure to follow) */
|
||||
/** Project context from config (constraints/background for AI, not to be included in output) */
|
||||
context: string | undefined;
|
||||
/** Artifact-specific rules from config (constraints for AI, not to be included in output) */
|
||||
rules: string[] | undefined;
|
||||
/** Template content (structure to follow - this IS the output format) */
|
||||
template: string;
|
||||
/** Dependencies with completion status and paths */
|
||||
dependencies: DependencyInfo[];
|
||||
@@ -218,14 +222,11 @@ export function generateInstructions(
|
||||
const dependencies = getDependencyInfo(artifact, context.graph, context.completed);
|
||||
const unlocks = getUnlockedArtifacts(context.graph, artifactId);
|
||||
|
||||
// Build enriched template with project config injections
|
||||
let enrichedTemplate = '';
|
||||
let projectConfig = null;
|
||||
|
||||
// Use projectRoot from context if not explicitly provided
|
||||
const effectiveProjectRoot = projectRoot ?? context.projectRoot;
|
||||
|
||||
// Try to read project config
|
||||
// Try to read project config for context and rules
|
||||
let projectConfig = null;
|
||||
if (effectiveProjectRoot) {
|
||||
try {
|
||||
projectConfig = readProjectConfig(effectiveProjectRoot);
|
||||
@@ -252,23 +253,10 @@ export function generateInstructions(
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Add context (all artifacts)
|
||||
if (projectConfig?.context) {
|
||||
enrichedTemplate += `<context>\n${projectConfig.context}\n</context>\n\n`;
|
||||
}
|
||||
|
||||
// 2. Add rules (only for matching artifact)
|
||||
// Extract context and rules as separate fields (not prepended to template)
|
||||
const configContext = projectConfig?.context?.trim() || undefined;
|
||||
const rulesForArtifact = projectConfig?.rules?.[artifactId];
|
||||
if (rulesForArtifact && rulesForArtifact.length > 0) {
|
||||
enrichedTemplate += `<rules>\n`;
|
||||
for (const rule of rulesForArtifact) {
|
||||
enrichedTemplate += `- ${rule}\n`;
|
||||
}
|
||||
enrichedTemplate += `</rules>\n\n`;
|
||||
}
|
||||
|
||||
// 3. Add original template (without wrapper - CLI handles XML structure)
|
||||
enrichedTemplate += templateContent;
|
||||
const configRules = rulesForArtifact && rulesForArtifact.length > 0 ? rulesForArtifact : undefined;
|
||||
|
||||
return {
|
||||
changeName: context.changeName,
|
||||
@@ -278,7 +266,9 @@ export function generateInstructions(
|
||||
outputPath: artifact.generates,
|
||||
description: artifact.description,
|
||||
instruction: artifact.instruction,
|
||||
template: enrichedTemplate,
|
||||
context: configContext,
|
||||
rules: configRules,
|
||||
template: templateContent,
|
||||
dependencies,
|
||||
unlocks,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Amazon Q Developer Command Adapter
|
||||
*
|
||||
* Formats commands for Amazon Q Developer following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Amazon Q adapter for command generation.
|
||||
* File path: .amazonq/prompts/opsx-<id>.md
|
||||
* Frontmatter: description
|
||||
*/
|
||||
export const amazonQAdapter: ToolCommandAdapter = {
|
||||
toolId: 'amazon-q',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.amazonq', 'prompts', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Antigravity Command Adapter
|
||||
*
|
||||
* Formats commands for Antigravity following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Antigravity adapter for command generation.
|
||||
* File path: .agent/workflows/opsx-<id>.md
|
||||
* Frontmatter: description
|
||||
*/
|
||||
export const antigravityAdapter: ToolCommandAdapter = {
|
||||
toolId: 'antigravity',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.agent', 'workflows', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Auggie (Augment CLI) Command Adapter
|
||||
*
|
||||
* Formats commands for Auggie following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Auggie adapter for command generation.
|
||||
* File path: .augment/commands/opsx-<id>.md
|
||||
* Frontmatter: description, argument-hint
|
||||
*/
|
||||
export const auggieAdapter: ToolCommandAdapter = {
|
||||
toolId: 'auggie',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.augment', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Claude Code Command Adapter
|
||||
*
|
||||
* Formats commands for Claude Code following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Escapes a string value for safe YAML output.
|
||||
* Quotes the string if it contains special YAML characters.
|
||||
*/
|
||||
function escapeYamlValue(value: string): string {
|
||||
// Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
|
||||
const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
|
||||
if (needsQuoting) {
|
||||
// Use double quotes and escape internal double quotes and backslashes
|
||||
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
||||
return `"${escaped}"`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a tags array as a YAML array with proper escaping.
|
||||
*/
|
||||
function formatTagsArray(tags: string[]): string {
|
||||
const escapedTags = tags.map((tag) => escapeYamlValue(tag));
|
||||
return `[${escapedTags.join(', ')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code adapter for command generation.
|
||||
* File path: .claude/commands/opsx/<id>.md
|
||||
* Frontmatter: name, description, category, tags
|
||||
*/
|
||||
export const claudeAdapter: ToolCommandAdapter = {
|
||||
toolId: 'claude',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.claude', 'commands', 'opsx', `${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: ${escapeYamlValue(content.name)}
|
||||
description: ${escapeYamlValue(content.description)}
|
||||
category: ${escapeYamlValue(content.category)}
|
||||
tags: ${formatTagsArray(content.tags)}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Cline Command Adapter
|
||||
*
|
||||
* Formats commands for Cline following its workflow specification.
|
||||
* Cline uses markdown headers instead of YAML frontmatter.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Cline adapter for command generation.
|
||||
* File path: .clinerules/workflows/opsx-<id>.md
|
||||
* Format: Markdown header with description
|
||||
*/
|
||||
export const clineAdapter: ToolCommandAdapter = {
|
||||
toolId: 'cline',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.clinerules', 'workflows', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `# ${content.name}
|
||||
|
||||
${content.description}
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* CodeBuddy Command Adapter
|
||||
*
|
||||
* Formats commands for CodeBuddy following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* CodeBuddy adapter for command generation.
|
||||
* File path: .codebuddy/commands/opsx/<id>.md
|
||||
* Frontmatter: name, description, argument-hint
|
||||
*/
|
||||
export const codebuddyAdapter: ToolCommandAdapter = {
|
||||
toolId: 'codebuddy',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.codebuddy', 'commands', 'opsx', `${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: ${content.name}
|
||||
description: "${content.description}"
|
||||
argument-hint: "[command arguments]"
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Codex Command Adapter
|
||||
*
|
||||
* Formats commands for Codex following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Codex adapter for command generation.
|
||||
* File path: .codex/prompts/opsx-<id>.md
|
||||
* Frontmatter: description, argument-hint
|
||||
*/
|
||||
export const codexAdapter: ToolCommandAdapter = {
|
||||
toolId: 'codex',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.codex', 'prompts', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Continue Command Adapter
|
||||
*
|
||||
* Formats commands for Continue following its .prompt specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Continue adapter for command generation.
|
||||
* File path: .continue/prompts/opsx-<id>.prompt
|
||||
* Frontmatter: name, description, invokable
|
||||
*/
|
||||
export const continueAdapter: ToolCommandAdapter = {
|
||||
toolId: 'continue',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.continue', 'prompts', `opsx-${commandId}.prompt`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: opsx-${content.id}
|
||||
description: ${content.description}
|
||||
invokable: true
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* CoStrict Command Adapter
|
||||
*
|
||||
* Formats commands for CoStrict following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* CoStrict adapter for command generation.
|
||||
* File path: .cospec/openspec/commands/opsx-<id>.md
|
||||
* Frontmatter: description, argument-hint
|
||||
*/
|
||||
export const costrictAdapter: ToolCommandAdapter = {
|
||||
toolId: 'costrict',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.cospec', 'openspec', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: "${content.description}"
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Crush Command Adapter
|
||||
*
|
||||
* Formats commands for Crush following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Crush adapter for command generation.
|
||||
* File path: .crush/commands/opsx/<id>.md
|
||||
* Frontmatter: name, description, category, tags
|
||||
*/
|
||||
export const crushAdapter: ToolCommandAdapter = {
|
||||
toolId: 'crush',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.crush', 'commands', 'opsx', `${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
const tagsStr = content.tags.join(', ');
|
||||
return `---
|
||||
name: ${content.name}
|
||||
description: ${content.description}
|
||||
category: ${content.category}
|
||||
tags: [${tagsStr}]
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Cursor Command Adapter
|
||||
*
|
||||
* Formats commands for Cursor following its frontmatter specification.
|
||||
* Cursor uses a different frontmatter format and file naming convention.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Escapes a string value for safe YAML output.
|
||||
* Quotes the string if it contains special YAML characters.
|
||||
*/
|
||||
function escapeYamlValue(value: string): string {
|
||||
// Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
|
||||
const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
|
||||
if (needsQuoting) {
|
||||
// Use double quotes and escape internal double quotes and backslashes
|
||||
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
||||
return `"${escaped}"`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor adapter for command generation.
|
||||
* File path: .cursor/commands/opsx-<id>.md
|
||||
* Frontmatter: name (as /opsx-<id>), id, category, description
|
||||
*/
|
||||
export const cursorAdapter: ToolCommandAdapter = {
|
||||
toolId: 'cursor',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.cursor', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: /opsx-${content.id}
|
||||
id: opsx-${content.id}
|
||||
category: ${escapeYamlValue(content.category)}
|
||||
description: ${escapeYamlValue(content.description)}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Factory Droid Command Adapter
|
||||
*
|
||||
* Formats commands for Factory Droid following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Factory adapter for command generation.
|
||||
* File path: .factory/commands/opsx-<id>.md
|
||||
* Frontmatter: description, argument-hint
|
||||
*/
|
||||
export const factoryAdapter: ToolCommandAdapter = {
|
||||
toolId: 'factory',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.factory', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
argument-hint: command arguments
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Gemini CLI Command Adapter
|
||||
*
|
||||
* Formats commands for Gemini CLI following its TOML specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Gemini adapter for command generation.
|
||||
* File path: .gemini/commands/opsx/<id>.toml
|
||||
* Format: TOML with description and prompt fields
|
||||
*/
|
||||
export const geminiAdapter: ToolCommandAdapter = {
|
||||
toolId: 'gemini',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.gemini', 'commands', 'opsx', `${commandId}.toml`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `description = "${content.description}"
|
||||
|
||||
prompt = """
|
||||
${content.body}
|
||||
"""
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* GitHub Copilot Command Adapter
|
||||
*
|
||||
* Formats commands for GitHub Copilot following its .prompt.md specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* GitHub Copilot adapter for command generation.
|
||||
* File path: .github/prompts/opsx-<id>.prompt.md
|
||||
* Frontmatter: description
|
||||
*/
|
||||
export const githubCopilotAdapter: ToolCommandAdapter = {
|
||||
toolId: 'github-copilot',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.github', 'prompts', `opsx-${commandId}.prompt.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* iFlow Command Adapter
|
||||
*
|
||||
* Formats commands for iFlow following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* iFlow adapter for command generation.
|
||||
* File path: .iflow/commands/opsx-<id>.md
|
||||
* Frontmatter: name, id, category, description
|
||||
*/
|
||||
export const iflowAdapter: ToolCommandAdapter = {
|
||||
toolId: 'iflow',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.iflow', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: /opsx-${content.id}
|
||||
id: opsx-${content.id}
|
||||
category: ${content.category}
|
||||
description: ${content.description}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Command Adapters Index
|
||||
*
|
||||
* Re-exports all tool command adapters.
|
||||
*/
|
||||
|
||||
export { amazonQAdapter } from './amazon-q.js';
|
||||
export { antigravityAdapter } from './antigravity.js';
|
||||
export { auggieAdapter } from './auggie.js';
|
||||
export { claudeAdapter } from './claude.js';
|
||||
export { clineAdapter } from './cline.js';
|
||||
export { codexAdapter } from './codex.js';
|
||||
export { codebuddyAdapter } from './codebuddy.js';
|
||||
export { continueAdapter } from './continue.js';
|
||||
export { costrictAdapter } from './costrict.js';
|
||||
export { crushAdapter } from './crush.js';
|
||||
export { cursorAdapter } from './cursor.js';
|
||||
export { factoryAdapter } from './factory.js';
|
||||
export { geminiAdapter } from './gemini.js';
|
||||
export { githubCopilotAdapter } from './github-copilot.js';
|
||||
export { iflowAdapter } from './iflow.js';
|
||||
export { kilocodeAdapter } from './kilocode.js';
|
||||
export { opencodeAdapter } from './opencode.js';
|
||||
export { qoderAdapter } from './qoder.js';
|
||||
export { qwenAdapter } from './qwen.js';
|
||||
export { roocodeAdapter } from './roocode.js';
|
||||
export { windsurfAdapter } from './windsurf.js';
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Kilo Code Command Adapter
|
||||
*
|
||||
* Formats commands for Kilo Code following its workflow specification.
|
||||
* Kilo Code workflows don't use frontmatter.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Kilo Code adapter for command generation.
|
||||
* File path: .kilocode/workflows/opsx-<id>.md
|
||||
* Format: Plain markdown without frontmatter
|
||||
*/
|
||||
export const kilocodeAdapter: ToolCommandAdapter = {
|
||||
toolId: 'kilocode',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.kilocode', 'workflows', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* OpenCode Command Adapter
|
||||
*
|
||||
* Formats commands for OpenCode following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* OpenCode adapter for command generation.
|
||||
* File path: .opencode/command/opsx-<id>.md
|
||||
* Frontmatter: description
|
||||
*/
|
||||
export const opencodeAdapter: ToolCommandAdapter = {
|
||||
toolId: 'opencode',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.opencode', 'command', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
description: ${content.description}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Qoder Command Adapter
|
||||
*
|
||||
* Formats commands for Qoder following its frontmatter specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Qoder adapter for command generation.
|
||||
* File path: .qoder/commands/opsx/<id>.md
|
||||
* Frontmatter: name, description, category, tags
|
||||
*/
|
||||
export const qoderAdapter: ToolCommandAdapter = {
|
||||
toolId: 'qoder',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.qoder', 'commands', 'opsx', `${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
const tagsStr = content.tags.join(', ');
|
||||
return `---
|
||||
name: ${content.name}
|
||||
description: ${content.description}
|
||||
category: ${content.category}
|
||||
tags: [${tagsStr}]
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Qwen Code Command Adapter
|
||||
*
|
||||
* Formats commands for Qwen Code following its TOML specification.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Qwen adapter for command generation.
|
||||
* File path: .qwen/commands/opsx-<id>.toml
|
||||
* Format: TOML with description and prompt fields
|
||||
*/
|
||||
export const qwenAdapter: ToolCommandAdapter = {
|
||||
toolId: 'qwen',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.qwen', 'commands', `opsx-${commandId}.toml`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `description = "${content.description}"
|
||||
|
||||
prompt = """
|
||||
${content.body}
|
||||
"""
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* RooCode Command Adapter
|
||||
*
|
||||
* Formats commands for RooCode following its workflow specification.
|
||||
* RooCode uses markdown headers instead of YAML frontmatter.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* RooCode adapter for command generation.
|
||||
* File path: .roo/commands/opsx-<id>.md
|
||||
* Format: Markdown header with description
|
||||
*/
|
||||
export const roocodeAdapter: ToolCommandAdapter = {
|
||||
toolId: 'roocode',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.roo', 'commands', `opsx-${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `# ${content.name}
|
||||
|
||||
${content.description}
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Windsurf Command Adapter
|
||||
*
|
||||
* Formats commands for Windsurf following its frontmatter specification.
|
||||
* Windsurf uses a similar format to Claude but may have different conventions.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import type { CommandContent, ToolCommandAdapter } from '../types.js';
|
||||
|
||||
/**
|
||||
* Escapes a string value for safe YAML output.
|
||||
* Quotes the string if it contains special YAML characters.
|
||||
*/
|
||||
function escapeYamlValue(value: string): string {
|
||||
// Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
|
||||
const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
|
||||
if (needsQuoting) {
|
||||
// Use double quotes and escape internal double quotes and backslashes
|
||||
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
||||
return `"${escaped}"`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a tags array as a YAML array with proper escaping.
|
||||
*/
|
||||
function formatTagsArray(tags: string[]): string {
|
||||
const escapedTags = tags.map((tag) => escapeYamlValue(tag));
|
||||
return `[${escapedTags.join(', ')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Windsurf adapter for command generation.
|
||||
* File path: .windsurf/commands/opsx/<id>.md
|
||||
* Frontmatter: name, description, category, tags
|
||||
*/
|
||||
export const windsurfAdapter: ToolCommandAdapter = {
|
||||
toolId: 'windsurf',
|
||||
|
||||
getFilePath(commandId: string): string {
|
||||
return path.join('.windsurf', 'commands', 'opsx', `${commandId}.md`);
|
||||
},
|
||||
|
||||
formatFile(content: CommandContent): string {
|
||||
return `---
|
||||
name: ${escapeYamlValue(content.name)}
|
||||
description: ${escapeYamlValue(content.description)}
|
||||
category: ${escapeYamlValue(content.category)}
|
||||
tags: ${formatTagsArray(content.tags)}
|
||||
---
|
||||
|
||||
${content.body}
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Command Generator
|
||||
*
|
||||
* Functions for generating command files using tool adapters.
|
||||
*/
|
||||
|
||||
import type { CommandContent, ToolCommandAdapter, GeneratedCommand } from './types.js';
|
||||
|
||||
/**
|
||||
* Generate a single command file using the provided adapter.
|
||||
* @param content - The tool-agnostic command content
|
||||
* @param adapter - The tool-specific adapter
|
||||
* @returns Generated command with path and file content
|
||||
*/
|
||||
export function generateCommand(
|
||||
content: CommandContent,
|
||||
adapter: ToolCommandAdapter
|
||||
): GeneratedCommand {
|
||||
return {
|
||||
path: adapter.getFilePath(content.id),
|
||||
fileContent: adapter.formatFile(content),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate multiple command files using the provided adapter.
|
||||
* @param contents - Array of tool-agnostic command contents
|
||||
* @param adapter - The tool-specific adapter
|
||||
* @returns Array of generated commands with paths and file contents
|
||||
*/
|
||||
export function generateCommands(
|
||||
contents: CommandContent[],
|
||||
adapter: ToolCommandAdapter
|
||||
): GeneratedCommand[] {
|
||||
return contents.map((content) => generateCommand(content, adapter));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Command Generation Module
|
||||
*
|
||||
* Generic command generation system with tool-specific adapters.
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* import { generateCommands, CommandAdapterRegistry, type CommandContent } from './command-generation/index.js';
|
||||
*
|
||||
* const contents: CommandContent[] = [...];
|
||||
* const adapter = CommandAdapterRegistry.get('cursor');
|
||||
* if (adapter) {
|
||||
* const commands = generateCommands(contents, adapter);
|
||||
* // Write commands to disk
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
// Types
|
||||
export type {
|
||||
CommandContent,
|
||||
ToolCommandAdapter,
|
||||
GeneratedCommand,
|
||||
} from './types.js';
|
||||
|
||||
// Registry
|
||||
export { CommandAdapterRegistry } from './registry.js';
|
||||
|
||||
// Generator functions
|
||||
export { generateCommand, generateCommands } from './generator.js';
|
||||
|
||||
// Adapters (for direct access if needed)
|
||||
export { claudeAdapter, cursorAdapter, windsurfAdapter } from './adapters/index.js';
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Command Adapter Registry
|
||||
*
|
||||
* Centralized registry for tool command adapters.
|
||||
* Similar pattern to existing SlashCommandRegistry in the codebase.
|
||||
*/
|
||||
|
||||
import type { ToolCommandAdapter } from './types.js';
|
||||
import { amazonQAdapter } from './adapters/amazon-q.js';
|
||||
import { antigravityAdapter } from './adapters/antigravity.js';
|
||||
import { auggieAdapter } from './adapters/auggie.js';
|
||||
import { claudeAdapter } from './adapters/claude.js';
|
||||
import { clineAdapter } from './adapters/cline.js';
|
||||
import { codexAdapter } from './adapters/codex.js';
|
||||
import { codebuddyAdapter } from './adapters/codebuddy.js';
|
||||
import { continueAdapter } from './adapters/continue.js';
|
||||
import { costrictAdapter } from './adapters/costrict.js';
|
||||
import { crushAdapter } from './adapters/crush.js';
|
||||
import { cursorAdapter } from './adapters/cursor.js';
|
||||
import { factoryAdapter } from './adapters/factory.js';
|
||||
import { geminiAdapter } from './adapters/gemini.js';
|
||||
import { githubCopilotAdapter } from './adapters/github-copilot.js';
|
||||
import { iflowAdapter } from './adapters/iflow.js';
|
||||
import { kilocodeAdapter } from './adapters/kilocode.js';
|
||||
import { opencodeAdapter } from './adapters/opencode.js';
|
||||
import { qoderAdapter } from './adapters/qoder.js';
|
||||
import { qwenAdapter } from './adapters/qwen.js';
|
||||
import { roocodeAdapter } from './adapters/roocode.js';
|
||||
import { windsurfAdapter } from './adapters/windsurf.js';
|
||||
|
||||
/**
|
||||
* Registry for looking up tool command adapters.
|
||||
*/
|
||||
export class CommandAdapterRegistry {
|
||||
private static adapters: Map<string, ToolCommandAdapter> = new Map();
|
||||
|
||||
// Static initializer - register built-in adapters
|
||||
static {
|
||||
CommandAdapterRegistry.register(amazonQAdapter);
|
||||
CommandAdapterRegistry.register(antigravityAdapter);
|
||||
CommandAdapterRegistry.register(auggieAdapter);
|
||||
CommandAdapterRegistry.register(claudeAdapter);
|
||||
CommandAdapterRegistry.register(clineAdapter);
|
||||
CommandAdapterRegistry.register(codexAdapter);
|
||||
CommandAdapterRegistry.register(codebuddyAdapter);
|
||||
CommandAdapterRegistry.register(continueAdapter);
|
||||
CommandAdapterRegistry.register(costrictAdapter);
|
||||
CommandAdapterRegistry.register(crushAdapter);
|
||||
CommandAdapterRegistry.register(cursorAdapter);
|
||||
CommandAdapterRegistry.register(factoryAdapter);
|
||||
CommandAdapterRegistry.register(geminiAdapter);
|
||||
CommandAdapterRegistry.register(githubCopilotAdapter);
|
||||
CommandAdapterRegistry.register(iflowAdapter);
|
||||
CommandAdapterRegistry.register(kilocodeAdapter);
|
||||
CommandAdapterRegistry.register(opencodeAdapter);
|
||||
CommandAdapterRegistry.register(qoderAdapter);
|
||||
CommandAdapterRegistry.register(qwenAdapter);
|
||||
CommandAdapterRegistry.register(roocodeAdapter);
|
||||
CommandAdapterRegistry.register(windsurfAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a tool command adapter.
|
||||
* @param adapter - The adapter to register
|
||||
*/
|
||||
static register(adapter: ToolCommandAdapter): void {
|
||||
CommandAdapterRegistry.adapters.set(adapter.toolId, adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an adapter by tool ID.
|
||||
* @param toolId - The tool identifier (e.g., 'claude', 'cursor')
|
||||
* @returns The adapter or undefined if not registered
|
||||
*/
|
||||
static get(toolId: string): ToolCommandAdapter | undefined {
|
||||
return CommandAdapterRegistry.adapters.get(toolId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered adapters.
|
||||
* @returns Array of all registered adapters
|
||||
*/
|
||||
static getAll(): ToolCommandAdapter[] {
|
||||
return Array.from(CommandAdapterRegistry.adapters.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an adapter is registered for a tool.
|
||||
* @param toolId - The tool identifier
|
||||
* @returns True if an adapter exists
|
||||
*/
|
||||
static has(toolId: string): boolean {
|
||||
return CommandAdapterRegistry.adapters.has(toolId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Command Generation Types
|
||||
*
|
||||
* Tool-agnostic interfaces for command generation.
|
||||
* These types separate "what to generate" from "how to format it".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tool-agnostic command data.
|
||||
* Represents the content of a command without any tool-specific formatting.
|
||||
*/
|
||||
export interface CommandContent {
|
||||
/** Command identifier (e.g., 'explore', 'apply', 'new') */
|
||||
id: string;
|
||||
/** Human-readable name (e.g., 'OpenSpec Explore') */
|
||||
name: string;
|
||||
/** Brief description of command purpose */
|
||||
description: string;
|
||||
/** Grouping category (e.g., 'Workflow') */
|
||||
category: string;
|
||||
/** Array of tag strings */
|
||||
tags: string[];
|
||||
/** The command instruction content (body text) */
|
||||
body: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-tool formatting strategy.
|
||||
* Each AI tool implements this interface to handle its specific file path
|
||||
* and frontmatter format requirements.
|
||||
*/
|
||||
export interface ToolCommandAdapter {
|
||||
/** Tool identifier matching AIToolOption.value (e.g., 'claude', 'cursor') */
|
||||
toolId: string;
|
||||
/**
|
||||
* Returns the relative file path for a command.
|
||||
* @param commandId - The command identifier (e.g., 'explore')
|
||||
* @returns Relative path from project root (e.g., '.claude/commands/opsx/explore.md')
|
||||
*/
|
||||
getFilePath(commandId: string): string;
|
||||
/**
|
||||
* Formats the complete file content including frontmatter.
|
||||
* @param content - The tool-agnostic command content
|
||||
* @returns Complete file content ready to write
|
||||
*/
|
||||
formatFile(content: CommandContent): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of generating a command file.
|
||||
*/
|
||||
export interface GeneratedCommand {
|
||||
/** Relative file path from project root */
|
||||
path: string;
|
||||
/** Complete file content (frontmatter + body) */
|
||||
fileContent: string;
|
||||
}
|
||||
+27
-187
@@ -1,199 +1,39 @@
|
||||
import { stringify as stringifyYaml } from 'yaml';
|
||||
import { listSchemasWithInfo, resolveSchema } from './artifact-graph/resolver.js';
|
||||
import type { ProjectConfig } from './project-config.js';
|
||||
|
||||
/**
|
||||
* Check if an error is an ExitPromptError (user cancelled with Ctrl+C).
|
||||
* Used instead of instanceof check since @inquirer modules use dynamic imports.
|
||||
*/
|
||||
export function isExitPromptError(error: unknown): boolean {
|
||||
return (
|
||||
error !== null &&
|
||||
typeof error === 'object' &&
|
||||
'name' in error &&
|
||||
(error as { name: string }).name === 'ExitPromptError'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of interactive config creation prompts.
|
||||
*/
|
||||
export interface ConfigPromptResult {
|
||||
/** Whether to create config file */
|
||||
createConfig: boolean;
|
||||
/** Selected schema name */
|
||||
schema?: string;
|
||||
/** Project context (optional) */
|
||||
context?: string;
|
||||
/** Per-artifact rules (optional) */
|
||||
rules?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to create project config interactively.
|
||||
* Used by experimental setup command.
|
||||
*
|
||||
* @param projectRoot - Optional project root for project-local schema resolution
|
||||
* @returns Config prompt result
|
||||
* @throws ExitPromptError if user cancels (Ctrl+C)
|
||||
*/
|
||||
export async function promptForConfig(
|
||||
projectRoot?: string
|
||||
): Promise<ConfigPromptResult> {
|
||||
// Dynamic imports to prevent pre-commit hook hangs (see #367)
|
||||
const { confirm, select, editor, checkbox } = await import('@inquirer/prompts');
|
||||
|
||||
// Ask if user wants to create config
|
||||
const shouldCreate = await confirm({
|
||||
message: 'Create openspec/config.yaml?',
|
||||
default: true,
|
||||
});
|
||||
|
||||
if (!shouldCreate) {
|
||||
return { createConfig: false };
|
||||
}
|
||||
|
||||
// Get available schemas
|
||||
const schemas = listSchemasWithInfo(projectRoot);
|
||||
|
||||
if (schemas.length === 0) {
|
||||
throw new Error('No schemas found. Cannot create config.');
|
||||
}
|
||||
|
||||
// Prompt for schema selection
|
||||
const selectedSchema = await select({
|
||||
message: 'Default schema for new changes?',
|
||||
choices: schemas.map((s) => ({
|
||||
name: `${s.name} (${s.artifacts.join(' → ')})`,
|
||||
value: s.name,
|
||||
description: s.description || undefined,
|
||||
})),
|
||||
});
|
||||
|
||||
// Prompt for project context
|
||||
console.log('\nAdd project context? (optional)');
|
||||
console.log('Context is shown to AI when creating artifacts.');
|
||||
console.log('Examples: tech stack, conventions, style guides, domain knowledge\n');
|
||||
|
||||
const contextInput = await editor({
|
||||
message: 'Press Enter to skip, or edit context:',
|
||||
default: '',
|
||||
waitForUseInput: false,
|
||||
});
|
||||
|
||||
const context = contextInput.trim() || undefined;
|
||||
|
||||
// Prompt for per-artifact rules
|
||||
const addRules = await confirm({
|
||||
message: 'Add per-artifact rules? (optional)',
|
||||
default: false,
|
||||
});
|
||||
|
||||
let rules: Record<string, string[]> | undefined;
|
||||
|
||||
if (addRules) {
|
||||
// Load the selected schema to get artifact list
|
||||
const schema = resolveSchema(selectedSchema, projectRoot);
|
||||
const artifactIds = schema.artifacts.map((a) => a.id);
|
||||
|
||||
// Let user select which artifacts to add rules for
|
||||
const selectedArtifacts = await checkbox({
|
||||
message: 'Which artifacts should have custom rules?',
|
||||
choices: artifactIds.map((id) => ({
|
||||
name: id,
|
||||
value: id,
|
||||
})),
|
||||
});
|
||||
|
||||
if (selectedArtifacts.length > 0) {
|
||||
rules = {};
|
||||
|
||||
// For each selected artifact, collect rules line by line
|
||||
for (const artifactId of selectedArtifacts) {
|
||||
const artifactRules = await promptForArtifactRules(artifactId);
|
||||
if (artifactRules.length > 0) {
|
||||
rules[artifactId] = artifactRules;
|
||||
}
|
||||
}
|
||||
|
||||
// If no rules were actually added, set to undefined
|
||||
if (Object.keys(rules).length === 0) {
|
||||
rules = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createConfig: true,
|
||||
schema: selectedSchema,
|
||||
context,
|
||||
rules,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt for rules for a specific artifact.
|
||||
* Collects rules one per line until user enters empty line.
|
||||
*
|
||||
* @param artifactId - The artifact ID to collect rules for
|
||||
* @returns Array of rules
|
||||
*/
|
||||
async function promptForArtifactRules(artifactId: string): Promise<string[]> {
|
||||
// Dynamic import to prevent pre-commit hook hangs (see #367)
|
||||
const { input } = await import('@inquirer/prompts');
|
||||
|
||||
const rules: string[] = [];
|
||||
|
||||
console.log(`\nRules for ${artifactId} artifact:`);
|
||||
console.log('Enter rules one per line, press Enter on empty line to finish:\n');
|
||||
|
||||
while (true) {
|
||||
const rule = await input({
|
||||
message: '│',
|
||||
validate: () => {
|
||||
// Empty string is valid (signals end of input)
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const trimmed = rule.trim();
|
||||
|
||||
// Empty line signals end of input
|
||||
if (!trimmed) {
|
||||
break;
|
||||
}
|
||||
|
||||
rules.push(trimmed);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize config to YAML string with proper multi-line formatting.
|
||||
* Serialize config to YAML string with helpful comments.
|
||||
*
|
||||
* @param config - Partial config object (schema required, context/rules optional)
|
||||
* @returns YAML string ready to write to file
|
||||
*/
|
||||
export function serializeConfig(config: Partial<ProjectConfig>): string {
|
||||
// Build clean config object (only include defined fields)
|
||||
const cleanConfig: Record<string, unknown> = {
|
||||
schema: config.schema,
|
||||
};
|
||||
const lines: string[] = [];
|
||||
|
||||
if (config.context) {
|
||||
cleanConfig.context = config.context;
|
||||
}
|
||||
// Schema (required)
|
||||
lines.push(`schema: ${config.schema}`);
|
||||
lines.push('');
|
||||
|
||||
if (config.rules && Object.keys(config.rules).length > 0) {
|
||||
cleanConfig.rules = config.rules;
|
||||
}
|
||||
// Context section with comments
|
||||
lines.push('# Project context (optional)');
|
||||
lines.push('# This is shown to AI when creating artifacts.');
|
||||
lines.push('# Add your tech stack, conventions, style guides, domain knowledge, etc.');
|
||||
lines.push('# Example:');
|
||||
lines.push('# context: |');
|
||||
lines.push('# Tech stack: TypeScript, React, Node.js');
|
||||
lines.push('# We use conventional commits');
|
||||
lines.push('# Domain: e-commerce platform');
|
||||
lines.push('');
|
||||
|
||||
// Serialize to YAML with proper formatting
|
||||
return stringifyYaml(cleanConfig, {
|
||||
indent: 2,
|
||||
lineWidth: 0, // Don't wrap long lines
|
||||
defaultStringType: 'PLAIN',
|
||||
defaultKeyType: 'PLAIN',
|
||||
});
|
||||
// Rules section with comments
|
||||
lines.push('# Per-artifact rules (optional)');
|
||||
lines.push('# Add custom rules for specific artifacts.');
|
||||
lines.push('# Example:');
|
||||
lines.push('# rules:');
|
||||
lines.push('# proposal:');
|
||||
lines.push('# - Keep proposals under 500 words');
|
||||
lines.push('# - Always include a "Non-goals" section');
|
||||
lines.push('# tasks:');
|
||||
lines.push('# - Break tasks into chunks of max 2 hours');
|
||||
|
||||
return lines.join('\n') + '\n';
|
||||
}
|
||||
|
||||
+22
-21
@@ -14,29 +14,30 @@ export interface AIToolOption {
|
||||
value: string;
|
||||
available: boolean;
|
||||
successLabel?: string;
|
||||
skillsDir?: string; // e.g., '.claude' - /skills suffix per Agent Skills spec
|
||||
}
|
||||
|
||||
export const AI_TOOLS: AIToolOption[] = [
|
||||
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer' },
|
||||
{ name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity' },
|
||||
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie' },
|
||||
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code' },
|
||||
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline' },
|
||||
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex' },
|
||||
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code' },
|
||||
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)' },
|
||||
{ name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict' },
|
||||
{ name: 'Crush', value: 'crush', available: true, successLabel: 'Crush' },
|
||||
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor' },
|
||||
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid' },
|
||||
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI' },
|
||||
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot' },
|
||||
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow' },
|
||||
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
|
||||
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
|
||||
{ name: 'Qoder (CLI)', value: 'qoder', available: true, successLabel: 'Qoder' },
|
||||
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code' },
|
||||
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode' },
|
||||
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf' },
|
||||
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer', skillsDir: '.amazonq' },
|
||||
{ name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity', skillsDir: '.agent' },
|
||||
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie', skillsDir: '.augment' },
|
||||
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code', skillsDir: '.claude' },
|
||||
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline' },
|
||||
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.codex' },
|
||||
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy' },
|
||||
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue' },
|
||||
{ name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict', skillsDir: '.cospec' },
|
||||
{ name: 'Crush', value: 'crush', available: true, successLabel: 'Crush', skillsDir: '.crush' },
|
||||
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor', skillsDir: '.cursor' },
|
||||
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid', skillsDir: '.factory' },
|
||||
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini' },
|
||||
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github' },
|
||||
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
|
||||
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
|
||||
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode' },
|
||||
{ name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder' },
|
||||
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
|
||||
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo' },
|
||||
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf', skillsDir: '.windsurf' },
|
||||
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
|
||||
];
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import path from 'path';
|
||||
import { ToolConfigurator } from './base.js';
|
||||
import { FileSystemUtils } from '../../utils/file-system.js';
|
||||
import { TemplateManager } from '../templates/index.js';
|
||||
import { OPENSPEC_MARKERS } from '../config.js';
|
||||
|
||||
export class AgentsStandardConfigurator implements ToolConfigurator {
|
||||
name = 'AGENTS.md standard';
|
||||
configFileName = 'AGENTS.md';
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, _openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getAgentsStandardTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export interface ToolConfigurator {
|
||||
name: string;
|
||||
configFileName: string;
|
||||
isAvailable: boolean;
|
||||
configure(projectPath: string, openspecDir: string): Promise<void>;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import path from 'path';
|
||||
import { ToolConfigurator } from './base.js';
|
||||
import { FileSystemUtils } from '../../utils/file-system.js';
|
||||
import { TemplateManager } from '../templates/index.js';
|
||||
import { OPENSPEC_MARKERS } from '../config.js';
|
||||
|
||||
export class ClaudeConfigurator implements ToolConfigurator {
|
||||
name = 'Claude Code';
|
||||
configFileName = 'CLAUDE.md';
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getClaudeTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import path from 'path';
|
||||
import { ToolConfigurator } from './base.js';
|
||||
import { FileSystemUtils } from '../../utils/file-system.js';
|
||||
import { TemplateManager } from '../templates/index.js';
|
||||
import { OPENSPEC_MARKERS } from '../config.js';
|
||||
|
||||
export class ClineConfigurator implements ToolConfigurator {
|
||||
name = 'Cline';
|
||||
configFileName = 'CLINE.md';
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getClineTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import path from 'path';
|
||||
import { ToolConfigurator } from './base.js';
|
||||
import { FileSystemUtils } from '../../utils/file-system.js';
|
||||
import { TemplateManager } from '../templates/index.js';
|
||||
import { OPENSPEC_MARKERS } from '../config.js';
|
||||
|
||||
export class CodeBuddyConfigurator implements ToolConfigurator {
|
||||
name = 'CodeBuddy';
|
||||
configFileName = 'CODEBUDDY.md';
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getClaudeTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import path from 'path';
|
||||
import { ToolConfigurator } from './base.js';
|
||||
import { FileSystemUtils } from '../../utils/file-system.js';
|
||||
import { TemplateManager } from '../templates/index.js';
|
||||
import { OPENSPEC_MARKERS } from '../config.js';
|
||||
|
||||
export class CostrictConfigurator implements ToolConfigurator {
|
||||
name = 'CoStrict';
|
||||
configFileName = 'COSTRICT.md';
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getCostrictTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import path from "path";
|
||||
import { ToolConfigurator } from "./base.js";
|
||||
import { FileSystemUtils } from "../../utils/file-system.js";
|
||||
import { TemplateManager } from "../templates/index.js";
|
||||
import { OPENSPEC_MARKERS } from "../config.js";
|
||||
|
||||
export class IflowConfigurator implements ToolConfigurator {
|
||||
name = "iFlow";
|
||||
configFileName = "IFLOW.md";
|
||||
isAvailable = true;
|
||||
|
||||
async configure(projectPath: string, openspecDir: string): Promise<void> {
|
||||
const filePath = path.join(projectPath, this.configFileName);
|
||||
const content = TemplateManager.getClaudeTemplate();
|
||||
|
||||
await FileSystemUtils.updateFileWithMarkers(
|
||||
filePath,
|
||||
content,
|
||||
OPENSPEC_MARKERS.start,
|
||||
OPENSPEC_MARKERS.end
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user