Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale e0be11dd85 Clarify release automation proposal 2025-10-02 00:54:14 +10:00
Tabish Bidiwale 9848242587 chore: add change proposals for agent scaffolding (#108) 2025-10-02 00:26:25 +10:00
Tabish Bidiwale 5e0d21d1ed chore: release 0.7.0 (#107) 2025-10-01 23:01:23 +10:00
Tabish Bidiwale 31d85d0e8b feat: Always install agentsmd (#106)
* Update CLI init to install root agents

* cleanup init command
2025-10-01 22:51:15 +10:00
Tabish Bidiwale bc3666d702 feat: Add Kilo Code workflow support (#105)
Implements Kilo Code integration with the following features:
- Added Kilo Code as a selectable AI tool in `openspec init`
- Created KiloCodeSlashCommandConfigurator to generate workflow files
- Generates `.kilocode/workflows/openspec-{proposal,apply,archive}.md`
- Added update support to refresh existing Kilo Code workflows
- Updated README with Kilo Code integration details
- Added comprehensive test coverage for init and update commands
- Updated CHANGELOG with new feature

All tasks from the change proposal are complete.
2025-10-01 19:01:10 +10:00
Tabish Bidiwale 970b9f6e2d Add Kilo Code workflow proposal (#103) 2025-10-01 13:21:22 +10:00
Tabish Bidiwale 5cb84a775e Add change proposal for Windsurf workflow support (#94)
* docs(windsurf): propose workflow support

* restore missing opencode spec
2025-10-01 11:51:07 +10:00
Tabish Bidiwale adc63069a9 chore(release): version packages (#100) 2025-09-30 17:35:29 +10:00
Tabish Bidiwale f8eca37796 feat(init): slim root agent instructions (#98)
* feat(init): slim root agent instructions

* Fix marker updates to ignore inline mentions

* styling updates

* update instructions

* fix tests
2025-09-30 17:03:56 +10:00
Tabish Bidiwale a908dc5a05 chore(release): version packages (#93)
Bump version to 0.5.0 with new features and improvements:
- E2E testing with cross-platform CI matrix
- Improved apply instructions
- Documentation improvements and cleanup
2025-09-29 23:47:22 +10:00
Tabish Bidiwale b46f99b9bc Make apply instructions more specific (#92) 2025-09-29 23:41:24 +10:00
Tabish Bidiwale 6f7cc2abd2 archive completed changes (#91) 2025-09-29 23:03:04 +10:00
Tabish Bidiwale 4867bfade5 feat: implement Phase 1 E2E testing with cross-platform CI matrix (#80)
* feat: implement Phase 1 E2E testing with cross-platform CI matrix

- Add shared runCLI helper in test/helpers/run-cli.ts for spawn testing
- Create test/cli-e2e/basic.test.ts covering help, version, validate flows
- Migrate existing CLI exec tests to use runCLI helper
- Extend CI matrix to bash (Linux/macOS) and pwsh (Windows)
- Update Phase 1 tasks and proposal with implementation status

* fix: correct YAML syntax in CI workflow diagnostics command

* fix: use multiline YAML for diagnostics command

* fix ci

* fix: ci

* fix: update core validation and json converter

* chore(ci): split pr and main workflows

* refactor: simplify CI workflow with unified matrix strategy

- Consolidate test_pr and test_matrix into single test job
- Add proper shell configuration with defaults
- Add timeout protection (15 minutes)
- Simplify required-checks to single job
- Maintain cross-platform testing (bash on Linux/macOS, pwsh on Windows)

* fix: restore lean PR workflow with async main branch matrix

- PRs run only essential tests on ubuntu-latest (fast feedback)
- Main branch runs full cross-platform matrix asynchronously
- Separate required-checks for each workflow type
- Different timeouts: 10min for PR, 15min for matrix
2025-09-29 22:20:30 +10:00
Tabish Bidiwale 7359b4846a docs(archive): document non-interactive flag (#90) 2025-09-29 15:59:51 +10:00
Tabish Bidiwale 88526e6b93 docs(readme): replace discord badge (#87) 2025-09-26 18:23:37 +10:00
Tabish Bidiwale 367aa12892 chore(release): version packages (#86) 2025-09-26 16:03:15 +10:00
James G. Best dcfb6afe0c feat: add Opencode slash commands (#83)
* First pass at adding Opencode slash commands

* Fix the agent

* Pass in Arguments to opencode slash commands

* Remove unneeded agents file
2025-09-26 01:37:30 +10:00
Tabish Bidiwale 86925b2b2d docs: add --yes flag to archive command template (#84) 2025-09-26 00:32:56 +10:00
Tabish Bidiwale 604ecb8bd1 fix: normalize line endings in markdown parser to handle CRLF files (#79)
Fixes validation errors on Windows by normalizing CRLF/CR line endings
to LF before parsing sections. Adds comprehensive test coverage for
CRLF handling in both unit and integration tests.
2025-09-25 15:59:09 +10:00
Tabish Bidiwale 5a4837c37d feat: add OpenSpec change proposals for CLI improvements (#78)
* feat: add CLI e2e testing improvement plan

## Summary
- Add phased approach to stabilize CLI spawn testing
- Expand cross-shell/OS matrix coverage when stable
- Optional packaging validation for CI environments

* feat: add markdown parser CRLF fix proposal and update e2e plan

- Add comprehensive proposal for fixing CRLF parsing issues on Windows
- Update CLI path references from dist/cli.js to dist/cli/index.js
- Streamline e2e testing tasks based on refined approach

* fix: correct spec delta to add parsing requirement instead of modifying remediation

- Change from MODIFIED to ADDED Requirements for cross-platform line ending parsing
- Create focused requirement for parser behavior rather than validation messages
- Maintain logical coherence between requirement and scenario
2025-09-25 14:53:21 +10:00
Tabish Bidiwale 9d9539aaa2 docs: add Discord badge (#73)
* docs: add discord badge

* chore: ignore ds store
2025-09-24 03:10:13 +10:00
Tabish Bidiwale c3fecf0619 chore: add codeowners (#72) 2025-09-19 11:56:37 +10:00
Tabish Bidiwale 6469593495 chore(release): publish to latest 2025-09-18 00:05:51 +10:00
Tabish Bidiwale 157936cf68 chore(release): prepare v0.3.0 2025-09-17 23:48:35 +10:00
Tabish Bidiwale fef33df753 Remove unused folders 2025-09-17 23:41:03 +10:00
Tabish Bidiwale 78b61e8466 Merge pull request #70 from Fission-AI/update-readme
docs: improve README Getting Started section
2025-09-17 23:28:44 +10:00
Tabish Bidiwale 5fa0fa68a7 fix tests 2025-09-17 23:27:04 +10:00
Tabish Bidiwale fe9eb44ec2 Remove md file 2025-09-17 23:14:57 +10:00
Tabish Bidiwale ee78f21b08 docs: improve README Getting Started section formatting and clarity 2025-09-17 23:12:40 +10:00
Tabish Bidiwale e3ae2ceaf0 feat(cli): polish init experience 2025-09-17 12:45:14 +10:00
Tabish Bidiwale 20b2fee749 feat(init): support multi-select extend flow 2025-09-17 11:36:56 +10:00
Tabish Bidiwale e7fff31df2 feat(cli): prepare init onboarding improvements 2025-09-17 10:45:22 +10:00
Tabish Bidiwale fdf9a30f0b Merge pull request #69 from Fission-AI/feat/add-agents-md-config
feat(cli): add agents md standard support
2025-09-17 10:25:49 +10:00
Tabish Bidiwale 161aa41cb3 style(cli): clarify agents option label 2025-09-17 10:18:51 +10:00
Tabish Bidiwale 9e092a185b style(cli): clarify init tool labels 2025-09-17 10:15:41 +10:00
Tabish Bidiwale 38454bb2a6 feat(cli): reorder init tool options 2025-09-17 10:12:40 +10:00
Tabish Bidiwale b04f1cc923 docs: note agents standard init option 2025-09-17 10:08:08 +10:00
Tabish Bidiwale ae86e9be9e chore(openspec): update agents tasks 2025-09-17 10:04:57 +10:00
Tabish Bidiwale f955e87fd9 feat(cli): add agents md configurator 2025-09-17 10:01:23 +10:00
Tabish Bidiwale 55efd19953 docs: add agents md config proposal 2025-09-17 09:50:56 +10:00
Tabish Bidiwale dab5d93b85 Merge pull request #68 from Fission-AI/codex/add-support-for-multiple-coding-agents
feat(cli-init): propose additional agent init flow
2025-09-17 08:34:45 +10:00
Tabish Bidiwale 7b0f494754 feat(cli-init): propose additional agent init flow 2025-09-17 08:33:10 +10:00
Tabish Bidiwale dd7ba71fe5 Merge pull request #67 from Fission-AI/codex/update-readme-for-custom-slash-commands
docs: correct claude code commands
2025-09-17 08:31:59 +10:00
Tabish Bidiwale 66ad5658f9 docs: correct claude code commands 2025-09-17 08:29:23 +10:00
Tabish Bidiwale 21a0e74b74 Merge pull request #66 from Fission-AI/changeset-release/main
chore(release): version packages
2025-09-16 16:50:42 +10:00
github-actions[bot] 485ef07ec7 Version Packages 2025-09-16 06:49:43 +00:00
Tabish Bidiwale ce5ceadbe7 chore(release): add changeset for dashboard release 2025-09-16 16:49:21 +10:00
Tabish Bidiwale 9a173b917c docs: refresh readme hero 2025-09-16 16:38:22 +10:00
Tabish Bidiwale 79baabbed1 Merge pull request #65 from Fission-AI/update-slash-commands
Update slash command guardrails
2025-09-16 15:48:02 +10:00
Tabish Bidiwale 818a5922ce docs: reference agents conventions in slash guardrails 2025-09-16 15:46:53 +10:00
Tabish Bidiwale d8d2930182 docs(templates): update slash command instructions 2025-09-16 15:05:59 +10:00
Tabish Bidiwale 6af6e0ccb6 Merge pull request #62 from Fission-AI/codex/implement-update-agent-file-name-change
feat: rename agent instructions file to AGENTS.md
2025-09-16 13:32:30 +10:00
Tabish Bidiwale 50e6660018 test: update update command logs 2025-09-16 13:28:36 +10:00
Tabish Bidiwale 4bbb52dda4 feat: rename agent instructions file 2025-09-16 13:17:49 +10:00
Tabish Bidiwale 4a0ae49b6e Merge pull request #64 from Fission-AI/codex/add-sorting-for-active-changes-by-completion
feat: add active change sorting proposal
2025-09-16 12:00:33 +10:00
Tabish Bidiwale 5b2049aedf feat(view): sort active changes by progress 2025-09-16 11:59:40 +10:00
Tabish Bidiwale 332020ac6b fix: clarify active change sorting tasks 2025-09-16 11:49:57 +10:00
Tabish Bidiwale 646c516b0d Merge pull request #63 from Fission-AI/feat/add-slash-command-support
feat(cli): add slash command support
2025-09-16 11:49:22 +10:00
Tabish Bidiwale 8c1b580f03 feat(cli): add slash command support 2025-09-16 11:36:00 +10:00
Tabish Bidiwale 17e6f7166b docs(readme): merge 'What You Get' into 'Why OpenSpec?' 2025-09-16 10:33:29 +10:00
Tabish Bidiwale 931d10477e Merge pull request #59 from Fission-AI/codex/rename-agent-instruction-file-to-agents.md
chore(changes): propose agent file rename
2025-09-16 10:26:37 +10:00
Tabish Bidiwale e4548bcc58 Merge pull request #60 from Fission-AI/codex/add-custom-slash-command-support-for-openspec
docs: add slash command support proposal
2025-09-16 08:41:10 +10:00
Tabish Bidiwale 68fc049955 docs(changes): use proposal/apply/archive names and per-tool slash naming; add format examples and test guidance 2025-09-16 08:39:39 +10:00
Tabish Bidiwale 4874a16495 feat(validate): propose scope-aware change validation (validate only existing artifacts) 2025-09-16 08:15:39 +10:00
Tabish Bidiwale 3caabf86cf docs(readme): regenerate from template via openspec update 2025-09-16 07:42:52 +10:00
Tabish Bidiwale d4593e4a54 docs(readme): clarify ADDED vs MODIFIED and update README template 2025-09-16 07:40:35 +10:00
Tabish Bidiwale 0144ec2b1b fix(changes): use ADDED for slash command requirements in cli-init and cli-update 2025-09-16 07:40:21 +10:00
Tabish Bidiwale 98d90cc00d Merge pull request #61 from Fission-AI/view-command-proposal
feat: add openspec view dashboard command
2025-09-12 21:33:45 +10:00
Tabish Bidiwale 8eb7ebd7cd docs: detail slash command instructions 2025-09-12 18:25:40 +10:00
Tabish Bidiwale 9d7b44b722 chore(changes): propose agent file rename 2025-09-10 10:56:08 +10:00
120 changed files with 4277 additions and 1090 deletions
-302
View File
@@ -1,302 +0,0 @@
---
description: OpenSpec conventions and workflow for Cursor agents working in this repo
alwaysApply: false
---
# OpenSpec Rules for Cursor Agents
Instructions for AI coding assistants using OpenSpec for spec-driven development.
## 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
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior
### Stage 2: Implementing Changes
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. **Mark complete immediately** - Update `- [x]` after each task
### 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] --skip-specs` for tooling-only changes
## 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
## 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 diff [change] # Show spec differences
openspec validate [item] # Validate changes or specs
openspec archive [change] # Archive after deployment
# 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
```
### Command Flags
- `--json` - Machine-readable output
- `--type change|spec` - Disambiguate items
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
## 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)
│ │ └── 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/[descriptive-name]/`
2. **Write proposal.md:**
```markdown
## 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]
```
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
```
## 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.
### 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.
## 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
# Debug delta parsing
openspec show [change] --json | jq '.deltas'
# Check specific requirement
openspec show [spec] --json -r 1
```
## 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"
## 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 diff [change] # What's changing?
openspec validate --strict # Is it correct?
openspec archive [change] # Mark complete
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.
+2
View File
@@ -0,0 +1,2 @@
# Default code ownership
* @TabishB
+89 -8
View File
@@ -15,10 +15,12 @@ concurrency:
cancel-in-progress: true
jobs:
test:
test_pr:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -48,7 +50,68 @@ jobs:
- name: Upload test coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
name: coverage-report-pr
path: coverage/
retention-days: 7
test_matrix:
name: Test (${{ matrix.label }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
if: github.event_name != 'pull_request'
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
shell: bash
label: linux-bash
- os: macos-latest
shell: bash
label: macos-bash
- os: windows-latest
shell: pwsh
label: windows-pwsh
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Print environment diagnostics
run: |
node -p "JSON.stringify({ platform: process.platform, arch: process.arch, shell: process.env.SHELL || process.env.ComSpec || '' })"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project
run: pnpm run build
- name: Run tests
run: pnpm test
- name: Upload test coverage
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
with:
name: coverage-report-main
path: coverage/
retention-days: 7
@@ -122,15 +185,15 @@ jobs:
echo "Changesets not configured, skipping validation"
fi
required-checks:
required-checks-pr:
name: All checks passed
runs-on: ubuntu-latest
needs: [test, lint]
if: always()
needs: [test_pr, lint]
if: always() && github.event_name == 'pull_request'
steps:
- name: Verify all checks passed
run: |
if [[ "${{ needs.test.result }}" != "success" ]]; then
if [[ "${{ needs.test_pr.result }}" != "success" ]]; then
echo "Test job failed"
exit 1
fi
@@ -138,4 +201,22 @@ jobs:
echo "Lint job failed"
exit 1
fi
echo "All required checks passed!"
echo "All required checks passed!"
required-checks-main:
name: All checks passed
runs-on: ubuntu-latest
needs: [test_matrix, lint]
if: always() && github.event_name != 'pull_request'
steps:
- name: Verify all checks passed
run: |
if [[ "${{ needs.test_matrix.result }}" != "success" ]]; then
echo "Matrix test job failed"
exit 1
fi
if [[ "${{ needs.lint.result }}" != "success" ]]; then
echo "Lint job failed"
exit 1
fi
echo "All required checks passed!"
+1 -1
View File
@@ -69,4 +69,4 @@ jobs:
- run: pnpm test
- name: Publish
run: pnpm publish --access public --provenance --no-git-checks --tag next
run: pnpm publish --access public --provenance --no-git-checks
+2 -1
View File
@@ -145,4 +145,5 @@ docs/
# Claude
.claude/
CLAUDE.md
CLAUDE.md
.DS_Store
View File
-4
View File
@@ -1,4 +0,0 @@
### Minor Changes
- 24b4866: Initial release
+4 -36
View File
@@ -1,40 +1,8 @@
<!-- OPENSPEC:START -->
# OpenSpec Project
# OpenSpec Instructions
This document provides instructions for AI coding assistants on how to use OpenSpec conventions for spec-driven development. Follow these rules precisely when working on OpenSpec-enabled projects.
This project uses OpenSpec to manage AI assistant workflows.
This project uses OpenSpec for spec-driven development. Specifications are the source of truth.
See @openspec/AGENTS.md for detailed conventions and guidelines.
- Full guidance lives in '@/openspec/AGENTS.md'.
- Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
## Complexity Management
**Default to minimal solutions:**
- Propose <100 lines of new code for features
- Prefer single-file implementations until proven insufficient
- Avoid frameworks, abstractions, and optimizations without clear justification
- Choose boring, well-understood patterns over novel approaches
**Question requests for complexity:**
- Caching? → Ask for performance data and targets
- New framework? → Suggest plain code first
- Extra layers? → Start with the thinnest viable design
**Justify complexity with data:**
- Performance metrics showing current solution is too slow
- Concrete scale requirements (e.g., >1000 users, >100MB data)
- Multiple proven use cases requiring an abstraction
## Package Manager
Always use pnpm (NOT npm or yarn) for all Node.js package management:
- Install dependencies: `pnpm install`
- Add packages: `pnpm add [package]`
- Run scripts: `pnpm run [script]`
## Git Commits
Use conventional commits with these rules:
- Format: `type(scope): subject` (e.g., `fix: resolve auth error`, `feat(api): add user endpoint`)
- Keep commit messages to ONE line only - no body or footer
- Common types: feat, fix, docs, style, refactor, test, chore
- Never add co-authorship lines or attribution
+63
View File
@@ -1,5 +1,68 @@
# @fission-ai/openspec
## 0.7.0
### Minor Changes
- Add native Kilo Code workflow integration so `openspec init` and `openspec update` manage `.kilocode/workflows/openspec-*.md` files.
- Always scaffold the managed root `AGENTS.md` hand-off stub and regroup the AI tool prompts during init/update to keep instructions consistent.
## 0.6.0
### Minor Changes
- Slim the generated root agent instructions down to a managed hand-off stub and update the init/update flows to refresh it safely.
## 0.5.0
### Minor Changes
- feat: implement Phase 1 E2E testing with cross-platform CI matrix
- Add shared runCLI helper in test/helpers/run-cli.ts for spawn testing
- Create test/cli-e2e/basic.test.ts covering help, version, validate flows
- Migrate existing CLI exec tests to use runCLI helper
- Extend CI matrix to bash (Linux/macOS) and pwsh (Windows)
- Split PR and main workflows for optimized feedback
### Patch Changes
- Make apply instructions more specific
Improve agent templates and slash command templates with more specific and actionable apply instructions.
- docs: improve documentation and cleanup
- Document non-interactive flag for archive command
- Replace discord badge in README
- Archive completed changes for better organization
## 0.4.0
### Minor Changes
- Add OpenSpec change proposals for CLI improvements and enhanced user experience
- Add Opencode slash commands support for AI-driven development workflows
### Patch Changes
- Add documentation improvements including --yes flag for archive command template and Discord badge
- Fix normalize line endings in markdown parser to handle CRLF files properly
## 0.3.0
### Minor Changes
- Enhance `openspec init` with extend mode, multi-tool selection, and an interactive `AGENTS.md` configurator.
## 0.2.0
### Minor Changes
- ce5cead: - Add an `openspec view` dashboard that rolls up spec counts and change progress at a glance
- Generate and update AI slash commands alongside the renamed `openspec/AGENTS.md` instructions file
- Remove the deprecated `openspec diff` command and direct users to `openspec show`
## 0.1.0
### Minor Changes
+173 -123
View File
@@ -15,140 +15,198 @@
<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/saTQQGQZ"><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/saTQQGQZ">OpenSpec Discord</a> for help and questions.
</p>
# OpenSpec
**Supported AI Tools:** ✅ Claude Code | 🔜 Cursor (coming soon) | 🔜 AGENTS.md support (coming soon)
Create **alignment** between humans and AI coding assistants through spec-driven development. **No API keys required.**
OpenSpec ensures you and your AI assistant agree on what to build before any code is written. By discussing and refining specifications first, you bring determinism to AI code generation, getting exactly what you want, not what the AI thinks you might want.
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?
**The Problem:** AI coding assistants are powerful but unpredictable. Without clear specifications, they generate code based on assumptions, often missing requirements or adding unwanted features. Teams waste time in review cycles because humans and AI aren't aligned on what to build.
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.
**The Solution:** OpenSpec creates alignment BEFORE code is written:
- **Human-AI Alignment** - You and your AI agree on specifications before implementation
- **Deterministic Output** - Clear specs lead to predictable code generation
- **Team Alignment** - Everyone reviews specs, not code surprises
- **Focus on What, Not How** - Define requirements while AI handles implementation
- **Living Documentation** - Specs evolve with your code as a natural byproduct
## What You Get
- **Alignment First** - Ensure humans and AI agree on what to build before writing code
- **Predictable AI Output** - Turn non-deterministic AI into a reliable development partner
- **Universal Tool Support** - Works with any AI assistant - Claude Code, Cursor, or future tools
- **No API Keys Required** - Integrates through context rules, not external services
- **Spec-Level Reviews** - Teams review intentions, not implementation details
- **Clear Feature Scope** - Know exactly what you're building and what you're not
- **Progress Tracking** - See what's proposed, in progress, or completed at a glance
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 It Works
```
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ SPECS │ │ CHANGES │ │ ARCHIVE │
│ (Truth) │◀──────│ (Proposals) │──────▶│ (Completed) │
└─────────────┘ └─────────────┘ └──────────────┘
▲ │ │
│ ▼ │
│ ┌─────────────┐ │
└───────────────│ CODE │◀──────────────┘
└─────────────┘
┌────────────────────┐
│ 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. SPECS define current capabilities (what IS built)
2. CHANGES propose modifications using deltas (what SHOULD change)
3. CODE implements the changes following tasks
4. ARCHIVE preserves completed changes after deployment
```
## Installation
### Prerequisites
- Node.js >= 20.19.0
### Install OpenSpec
Install globally:
```bash
npm install -g @fission-ai/openspec
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
### 1. Initialize OpenSpec in Your Project
### Supported AI Tools
#### Native Slash Commands
These tools have built-in OpenSpec commands. Select the OpenSpec integration when prompted.
| Tool | Commands |
|------|----------|
| **Claude Code** | `/openspec:proposal`, `/openspec:apply`, `/openspec:archive` |
| **Cursor** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **OpenCode** | `/openspec-proposal`, `/openspec-apply`, `/openspec-archive` |
| **Kilo Code** | `/openspec-proposal.md`, `/openspec-apply.md`, `/openspec-archive.md` (`.kilocode/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`.
#### AGENTS.md Compatible
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 |
|-------|
| Codex • Amp • Jules • Gemini CLI • GitHub Copilot • Others |
### Install & Initialize
#### Prerequisites
- **Node.js >= 20.19.0** - Check your version with `node --version`
#### Step 1: Install the CLI globally
```bash
# Navigate to your project
npm install -g @fission-ai/openspec@latest
```
Verify installation:
```bash
openspec --version
```
#### Step 2: Initialize OpenSpec in your project
Navigate to your project directory:
```bash
cd my-project
```
# Initialize OpenSpec
Run the initialization:
```bash
openspec init
# Select your AI tool (more coming soon!):
# "Which AI tool do you use?"
# > Claude Code
# Cursor (coming soon)
# This creates:
# openspec/
# ├── specs/ # Current specifications (truth)
# ├── changes/ # Proposed changes
# └── README.md # AI instructions for your tool
```
### 2. Create Your First Change
**What happens during initialization:**
- You'll be prompted to pick any natively supported AI tools (Claude Code, Cursor, OpenCode, 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
Jump straight into creating a change proposal with your AI assistant (works with Claude Code, Cursor, or any AI tool):
**After setup:**
- Primary AI tools can trigger `/openspec` workflows without additional configuration
- Run `openspec list` to verify the setup and view any active changes
```markdown
// Quick win - Add a simple new feature:
You: "I want to add a user profile API endpoint.
Please create an OpenSpec change proposal for this."
### Create Your First Change
AI: "I'll create an OpenSpec change proposal for the user profile API..."
*Creates openspec/changes/add-user-profile-api/ with:*
- proposal.md (why this feature is needed)
- tasks.md (implementation checklist)
- design.md (API design decisions)
- specs/user-profile/spec.md (new requirements)
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.
You: "The proposal looks good. Let's implement it."
#### 1. Draft the Proposal
Start by asking your AI to create a change proposal:
AI: "Following the tasks in openspec/changes/add-user-profile-api/tasks.md:
Task 1.1: Create user profile model..."
*Implements each task systematically*
```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.*
```
### 3. Track Your Work
#### 2. Verify & Review
Check that the change was created correctly and review the proposal:
```bash
# View active changes (what's being worked on)
openspec list
# Validate your changes are properly formatted
openspec validate add-2fa --strict
# After deployment, archive the completed change
openspec archive add-2fa
# This moves the change to archive/ and updates specs/
$ 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
```
## Common Commands
#### 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, Cursor) 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
# Most used:
openspec list # See what changes you're working on
openspec archive <change> # Mark a change as complete after deployment
# Also useful:
openspec validate <change> # Check formatting before committing
openspec show <change> # View change details
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
@@ -235,46 +293,38 @@ Deltas are "patches" that show how specs change:
- Every requirement needs at least one `#### Scenario:` block
- Use SHALL/MUST in requirement text
## Why OpenSpec Works
OpenSpec creates **alignment** between you and your AI coding assistant:
1. **You describe** what you want to build
2. **AI creates specs** before writing any code
3. **You review and adjust** the specifications
4. **AI implements** exactly what was specified
5. **Everyone understands** what's being built through clear specs
**True Interoperability:** OpenSpec is designed to be universal. No API keys, no vendor lock-in. It works by adding context rules to ANY AI coding tool - whether you use Claude Code today, switch to Cursor tomorrow, or adopt the next breakthrough AI assistant. Your specs remain portable and your workflow stays consistent.
## How OpenSpec Compares
### vs. Kiro.dev
OpenSpec groups all changes for a feature in one place (`openspec/changes/feature-name/`), making it easy to track what needs to be done. Kiro spreads changes across multiple spec folders, making feature tracking harder.
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 based on vague prompts, often missing requirements or adding unwanted features. OpenSpec ensures alignment before any code is written.
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
### Getting Started with Your Team
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, Cursor, or any AGENTS.md-compatible tool while sharing the same specs.
1. **Initialize OpenSpec** - Run `openspec init` in your project
2. **Start with new features** - Use OpenSpec for your next change proposal
3. **Build incrementally** - Each new feature adds to your spec library
4. **Future capability** - We're working on tools to generate specs from existing code
Run `openspec update` whenever someone switches tools so your agents pick up the latest instructions and slash-command bindings.
**Tool Freedom:** Your team can use different AI assistants. One developer might use Claude Code while another uses Cursor - OpenSpec keeps everyone aligned through shared specifications. Run `openspec update` to configure for any supported tool without affecting others.
## 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.
## Contributing
- Install dependencies: `npm install`
- Build: `npm run build`
- Test: `npm test`
- Develop CLI locally: `npm run dev` or `npm run dev:cli`
- 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`
## License
Binary file not shown.

After

Width:  |  Height:  |  Size: 450 KiB

+12 -4
View File
@@ -1,7 +1,15 @@
#!/usr/bin/env node
import { execSync } from 'child_process';
import { execFileSync } from 'child_process';
import { existsSync, rmSync } from 'fs';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const runTsc = (args = []) => {
const tscPath = require.resolve('typescript/bin/tsc');
execFileSync(process.execPath, [tscPath, ...args], { stdio: 'inherit' });
};
console.log('🔨 Building OpenSpec...\n');
@@ -14,10 +22,10 @@ if (existsSync('dist')) {
// Run TypeScript compiler (use local version explicitly)
console.log('Compiling TypeScript...');
try {
execSync('./node_modules/.bin/tsc -v', { stdio: 'inherit' });
execSync('./node_modules/.bin/tsc', { stdio: 'inherit' });
runTsc(['--version']);
runTsc();
console.log('\n✅ Build completed successfully!');
} catch (error) {
console.error('\n❌ Build failed!');
process.exit(1);
}
}
+29 -5
View File
@@ -40,20 +40,28 @@ Skip proposal for:
- 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` 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. **Mark complete immediately** - Update `- [x]` after each task
6. **Validate strictly** - Run `openspec validate [change] --strict` and address issues
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] --skip-specs` for tooling-only changes
- Use `openspec archive [change] --skip-specs --yes` for tooling-only changes
- Run `openspec validate --strict` to confirm the archived change passes checks
## Before Any Task
@@ -87,8 +95,9 @@ After deployment, create separate PR to:
openspec list # List active changes
openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec diff [change] # Show spec differences
openspec validate [item] # Validate changes or specs
openspec archive [change] # Archive after deployment
openspec archive [change] [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
@@ -110,6 +119,7 @@ openspec validate [change] --strict
- `--strict` - Comprehensive validation
- `--no-interactive` - Disable prompts
- `--skip-specs` - Archive without spec updates
- `--yes`/`-y` - Skip confirmation prompts (non-interactive archive)
## Directory Structure
@@ -255,6 +265,19 @@ Every requirement MUST have at least one scenario.
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
@@ -425,8 +448,9 @@ Only add complexity with:
```bash
openspec list # What's in progress?
openspec show [item] # View details
openspec diff [change] # What's changing?
openspec validate --strict # Is it correct?
openspec archive [change] # Mark complete
openspec archive [change] [--yes|-y] # Mark complete (add --yes for automation)
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.
@@ -0,0 +1,17 @@
## Why
- Kilo Code executes \"slash commands\" by loading markdown workflows from `.kilocode/workflows/` (or the global `~/.kilocode/workflows/`) and running them when a user types `/workflow-name.md`, making project-local workflow files the analogue to the slash-command files we already ship for other tools.\\
([Workflows | Kilo Code Docs](https://kilocode.ai/docs/features/slash-commands/workflows))
- Those workflows are plain markdown with step-by-step instructions that can call built-in tools and MCP integrations, so reusing OpenSpec's shared proposal/apply/archive bodies keeps behaviour aligned across assistants without inventing new content.
- OpenSpec already detects configured tools and refreshes marker-wrapped files during `init`/`update`; extending the same mechanism to `.kilocode/workflows/openspec-*.md` ensures Kilo Code stays in sync with one source of truth.
## What Changes
- Add Kilo Code to the `openspec init` tool picker with \"already configured\" detection, including wiring for extend mode so teams can refresh Kilo Code assets.
- Implement a `KiloCodeSlashCommandConfigurator` that creates `.kilocode/workflows/openspec-{proposal,apply,archive}.md`, ensuring the workflow directory exists and wrapping shared content in OpenSpec markers (no front matter required).
- Teach `openspec update` to refresh existing Kilo Code workflows (and only those that already exist) using the shared slash-command templates.
- Update documentation, release notes, and integration tests so the new workflow support is covered alongside Claude, Cursor, OpenCode, and Windsurf.
## Impact
- Specs: `cli-init`, `cli-update`
- Code: `src/core/config.ts`, `src/core/configurators/(registry|slash/*)`, `src/core/templates/slash-command-templates.ts`, CLI wiring for tool summaries
- Tests: init/update workflow coverage, regression for marker preservation in `.kilocode/workflows/`
- Docs: README / CHANGELOG updates advertising Kilo Code workflow support
@@ -0,0 +1,24 @@
## MODIFIED Requirements
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions using a marker system.
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** prompt the user with "Which AI tools do you use?" using a multi-select menu
- **AND** list every available tool with a checkbox:
- Claude Code (creates or refreshes CLAUDE.md and slash commands)
- Cursor (creates or refreshes `.cursor/commands/*` slash commands)
- OpenCode (creates or refreshes `.opencode/command/openspec-*.md` slash commands)
- Windsurf (creates or refreshes `.windsurf/workflows/openspec-*.md` workflows)
- Kilo Code (creates or refreshes `.kilocode/workflows/openspec-*.md` workflows)
- AGENTS.md standard (creates or refreshes AGENTS.md with OpenSpec markers)
- **AND** show "(already configured)" beside tools whose managed files exist so users understand selections will refresh content
- **AND** treat disabled tools as "coming soon" and keep them unselectable
- **AND** allow confirming with Enter after selecting one or more tools
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.
#### Scenario: Generating slash commands for Kilo Code
- **WHEN** the user selects Kilo Code during initialization
- **THEN** create `.kilocode/workflows/openspec-proposal.md`, `.kilocode/workflows/openspec-apply.md`, and `.kilocode/workflows/openspec-archive.md`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
@@ -0,0 +1,8 @@
## MODIFIED Requirements
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones.
#### Scenario: Updating slash commands for Kilo Code
- **WHEN** `.kilocode/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates wrapped in OpenSpec markers
- **AND** ensure templates include instructions for the relevant workflow stage
- **AND** skip creating missing files (the update command only refreshes what already exists)
@@ -0,0 +1,15 @@
## 1. CLI wiring
- [x] 1.1 Add Kilo Code to the selectable AI tools in `openspec init`, including "already configured" detection and success summaries.
- [x] 1.2 Register a `KiloCodeSlashCommandConfigurator` alongside other slash-command tools.
## 2. Workflow generation
- [x] 2.1 Implement the configurator so it creates `.kilocode/workflows/` (if needed) and writes `openspec-{proposal,apply,archive}.md` with OpenSpec markers.
- [x] 2.2 Reuse the shared slash-command bodies without front matter; verify resulting files stay Markdown-only with no extra metadata.
## 3. Update support
- [x] 3.1 Ensure `openspec update` refreshes existing Kilo Code workflows while skipping ones that are absent.
- [x] 3.2 Add regression coverage confirming marker content is replaced (not duplicated) during updates.
## 4. Documentation
- [x] 4.1 Update README / docs to note Kilo Code workflow support and path (`.kilocode/workflows/`).
- [x] 4.2 Mention the integration in CHANGELOG or release notes if applicable.
@@ -0,0 +1,11 @@
## Why
Manual setup for new changes leads to formatting mistakes in spec deltas and slows agents who must recreate the same file skeletons for every proposal. A built-in scaffold command will generate compliant templates so assistants can focus on the change content instead of structure.
## What Changes
- Add an `openspec scaffold <change-id>` CLI command that creates a change directory with validated `proposal.md`, `tasks.md`, and spec delta templates.
- Update CLI documentation and quick-reference guidance so agents discover the scaffold workflow before drafting files manually.
- Add automated coverage (unit/integ tests) to ensure the command respects existing naming rules and generated Markdown passes validation.
## Impact
- Affected specs: `specs/cli-scaffold`
- Affected code: `src/cli/index.ts`, `src/commands`, `docs/`
@@ -0,0 +1,44 @@
## ADDED Requirements
### Requirement: Scaffolding Command Registration
The CLI SHALL expose an `openspec scaffold <change-id>` command that validates the change identifier before generating files.
#### Scenario: Registering scaffold command
- **WHEN** a user runs `openspec scaffold add-user-notifications`
- **THEN** the CLI SHALL reject invalid identifiers (non kebab-case) before proceeding
- **AND** display usage documentation via `openspec scaffold --help`
- **AND** exit with code 0 after successful scaffolding
### Requirement: Change Directory Structure
The scaffold command SHALL create the standard change workspace with proposal, tasks, optional design, and delta directories laid out according to OpenSpec conventions.
#### Scenario: Generating change workspace
- **WHEN** scaffolding a new change with id `add-user-notifications`
- **THEN** create `openspec/changes/add-user-notifications/`
- **AND** generate `proposal.md`, `tasks.md`, and `design.md` (commented placeholder content) in that directory when missing
- **AND** create `openspec/changes/add-user-notifications/specs/` ready for capability-specific deltas
### Requirement: Template Content Guidance
The scaffold command SHALL populate generated Markdown files with OpenSpec-compliant templates so authors can copy, edit, and pass validation without reformatting.
#### Scenario: Populating proposal and tasks templates
- **WHEN** the scaffold command writes `proposal.md`
- **THEN** include the `## Why`, `## What Changes`, and `## Impact` headings with placeholder guidance text
- **AND** ensure `tasks.md` starts with `## 1. Implementation` and numbered checklist items using `- [ ]` syntax
- **AND** annotate optional sections (like `design.md`) with inline TODO comments so users understand when to keep or delete them
### Requirement: Delta Spec Creation
The scaffold command SHALL create at least one capability delta file with correctly formatted requirement and scenario placeholders that guide authors to enter the actual behavior.
#### Scenario: Creating spec delta skeleton
- **WHEN** scaffolding a change and the capability `cli-scaffold` is provided interactively or via flags
- **THEN** generate `openspec/changes/add-user-notifications/specs/cli-scaffold/spec.md`
- **AND** include `## ADDED Requirements` with at least one `### Requirement:` block and matching `#### Scenario:` entries that remind the author to replace placeholder text
- **AND** ensure the generated delta passes `openspec validate add-user-notifications --strict` until the author edits it
### Requirement: Idempotent Execution
The scaffold command SHALL be safe to rerun, preserving user edits while filling in any missing managed sections.
#### Scenario: Rerunning scaffold on existing change
- **WHEN** the command is executed again for an existing change directory containing user-edited files
- **THEN** leave existing content untouched except for managed placeholder regions or missing files that need creation
- **AND** update the filesystem summary to highlight which files were skipped, created, or refreshed
@@ -0,0 +1,11 @@
## 1. CLI scaffolding command
- [ ] 1.1 Register an `openspec scaffold` command in the CLI entrypoint with `change-id` argument validation.
- [ ] 1.2 Implement generator logic that creates the change directory structure plus default `proposal.md`, `tasks.md`, and delta spec skeletons without overwriting existing populated files.
## 2. Templates and documentation
- [ ] 2.1 Surface copy/paste templates and scaffold usage in the top-level quick reference for `openspec/AGENTS.md`.
- [ ] 2.2 Refresh other CLI docs (`docs/`, README) to mention the scaffold workflow and link to instructions.
## 3. Test coverage
- [ ] 3.1 Add unit tests covering name validation, file generation, and idempotent reruns.
- [ ] 3.2 Add integration coverage ensuring generated files pass `openspec validate --strict` without manual edits.
@@ -0,0 +1,17 @@
## Why
- Windsurf exposes "Workflows" as the vehicle for slash-like automation: saved Markdown files under `.windsurf/workflows/` that Cascade discovers across the workspace (including subdirectories and up to the git root), then executes when a user types `/workflow-name`. These files can be team-authored, must stay under 12k characters, and can call other workflows, making them the natural place to publish OpenSpec guidance for Windsurf users.\
([Windsurf Workflows documentation](https://docs.windsurf.com/windsurf/cascade/workflows))
- The Wave 12 changelog reiterates that workflows are invoked via slash commands and that Windsurf stores them in `.windsurf/workflows`, so the OpenSpec CLI just needs to generate Markdown there to participate in Windsurf's command palette.\
("Custom Workflows" section, [Windsurf changelog](https://windsurf.com/changelog))
- OpenSpec already ships shared command bodies for proposal/apply/archive and uses markers so commands stay up to date. Extending the same templates to Windsurf keeps behaviour consistent with Claude, Cursor, and OpenCode without inventing new content flows.
## What Changes
- Add Windsurf to the CLI tool picker (`openspec init`) and the slash-command registry so selecting it scaffolds `.windsurf/workflows/openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md` with marker-managed bodies.
- Shape each Windsurf workflow with a short heading/description plus the existing OpenSpec guardrails/steps wrapped in markers, ensuring the total payload remains well below the 12,000 character limit.
- Ensure `openspec update` refreshes existing Windsurf workflows (and only those that already exist) in-place, mirroring current behaviour for other editors.
- Extend unit tests for init/update to cover Windsurf generation and updates, and update the README/tooling docs to advertise Windsurf support.
## Impact
- Specs: `cli-init`, `cli-update`
- Code: `src/core/configurators/slash/*`, `src/core/templates/slash-command-templates.ts`, CLI prompts, README
- Tests: init/update integration coverage for Windsurf workflows
@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions using a marker system.
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** prompt the user with "Which AI tools do you use?" using a multi-select menu
- **AND** list every available tool with a checkbox:
- Claude Code (creates or refreshes CLAUDE.md and slash commands)
- Cursor (creates or refreshes `.cursor/commands/*` slash commands)
- OpenCode (creates or refreshes `.opencode/command/openspec-*.md` slash commands)
- Windsurf (creates or refreshes `.windsurf/workflows/openspec-*.md` workflows)
- AGENTS.md standard (creates or refreshes AGENTS.md with OpenSpec markers)
- **AND** show "(already configured)" beside tools whose managed files exist so users understand selections will refresh content
- **AND** treat disabled tools as "coming soon" and keep them unselectable
- **AND** allow confirming with Enter after selecting one or more tools
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.
#### Scenario: Generating slash commands for Windsurf
- **WHEN** the user selects Windsurf during initialization
- **THEN** create `.windsurf/workflows/openspec-proposal.md`, `.windsurf/workflows/openspec-apply.md`, and `.windsurf/workflows/openspec-archive.md`
- **AND** populate each file from shared templates (wrapped in OpenSpec markers) so workflow text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
@@ -0,0 +1,8 @@
## MODIFIED Requirements
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones.
#### Scenario: Updating slash commands for Windsurf
- **WHEN** `.windsurf/workflows/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates wrapped in OpenSpec markers
- **AND** ensure templates include instructions for the relevant workflow stage
- **AND** skip creating missing files (the update command only refreshes what already exists)
@@ -0,0 +1,17 @@
## 1. CLI wiring
- [ ] 1.1 Add Windsurf to the selectable AI tools in `openspec init`, including "already configured" detection.
- [ ] 1.2 Register a `WindsurfSlashCommandConfigurator` that writes workflows to `.windsurf/workflows/` and ensures the directory exists.
- [ ] 1.3 Ensure `openspec update` pulls the Windsurf configurator when winds is selected and skips creation when files are absent.
## 2. Workflow templates
- [ ] 2.1 Reuse the shared proposal/apply/archive bodies, adding Windsurf-specific headings/description before the OpenSpec markers.
- [ ] 2.2 Confirm generated Markdown (per file) stays comfortably under the 12k character ceiling noted in the Windsurf docs.
## 3. Tests & safeguards
- [ ] 3.1 Extend init tests to assert creation of `.windsurf/workflows/openspec-*.md` when Windsurf is chosen.
- [ ] 3.2 Extend update tests to assert existing Windsurf workflows are refreshed and non-existent files are ignored.
- [ ] 3.3 Add regression coverage for marker preservation inside Windsurf workflow files.
## 4. Documentation
- [ ] 4.1 Update README (and any user-facing docs) to list Windsurf under native slash/workflow integrations.
- [ ] 4.2 Call out Windsurf workflow support in release notes or CHANGELOG if applicable.
@@ -0,0 +1,28 @@
# Add AGENTS.md Standard Support To Init/Update
## Summary
- Teach `openspec init` to manage a root-level `AGENTS.md` file using the same marker system as `CLAUDE.md`.
- Allow `openspec update` to refresh or scaffold that root `AGENTS.md` so AGENTS-compatible tools always receive current instructions.
- Keep the existing `openspec/AGENTS.md` template as the canonical source while ensuring assistants that read `AGENTS.md` opt-in instructions get the latest guidance automatically.
## Motivation
The README now points teams to AGENTS.md-compatible assistants, but the CLI only manages `CLAUDE.md`. Projects must hand-roll a root `AGENTS.md` file to benefit from the standard, and updates will drift unless maintainers remember to copy content manually. Extending `init` and `update` closes that gap so OpenSpec actually delivers on the promise of first-class AGENTS support.
## Proposal
1. Extend the `openspec init` selection flow with an "AGENTS.md standard" option that creates or refreshes a root `AGENTS.md` file wrapped in OpenSpec markers, mirroring the existing CLAUDE integration.
2. When generating the file, pull the managed content from the same template used in `openspec/AGENTS.md`, ensuring both locations stay in sync.
3. Update `openspec update` so it always refreshes the root `AGENTS.md` (creating it if missing) alongside `openspec/AGENTS.md` and any other configured assistants.
4. Document the new behavior in CLI specs and verify marker handling (no duplicates, preserve user content outside the block) with tests for both commands.
## Out of Scope
- Adding additional AGENTS-specific prompts or workflows beyond the shared instructions block.
- Non-interactive flags or bulk configuration for multiple standards in one run.
- Broader restructuring of how templates are stored or loaded.
## Risks & Mitigations
- **Risk:** Accidentally overwriting user-edited content surrounding the managed block.
- **Mitigation:** Reuse the existing marker-update helper shared with `CLAUDE.md`, and add tests that cover files containing custom text before and after the block.
- **Risk:** Divergence between `openspec/AGENTS.md` and the root file.
- **Mitigation:** Source the root file content from the canonical template rather than duplicating strings inline.
- **Risk:** Confusion about when the file is created.
- **Mitigation:** Log creation vs update, and ensure help text references the AGENTS option during `init`.
@@ -0,0 +1,71 @@
## MODIFIED Requirements
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions based on user selection.
#### Scenario: Prompting for AI tool selection
- **WHEN** run
- **THEN** prompt user to select AI tools to configure:
- Claude Code (✅ OpenSpec custom slash commands available)
- Cursor (✅ OpenSpec custom slash commands available)
- AGENTS.md (works with Codex, Amp, Copilot, …)
### Requirement: AI Tool Configuration Details
The command SHALL properly configure selected AI tools with OpenSpec-specific instructions using a marker system.
#### Scenario: Configuring Claude Code
- **WHEN** Claude Code is selected
- **THEN** create or update `CLAUDE.md` in the project root directory (not inside openspec/)
#### Scenario: Configuring AGENTS standard
- **WHEN** the AGENTS.md standard is selected
- **THEN** create or update `AGENTS.md` in the project root directory (not inside openspec/)
#### Scenario: Creating new CLAUDE.md
- **WHEN** CLAUDE.md does not exist
- **THEN** create new file with OpenSpec content wrapped in markers:
```markdown
<!-- OPENSPEC:START -->
# OpenSpec Project
This document provides instructions for AI coding assistants on how to use OpenSpec conventions for spec-driven development. Follow these rules precisely when working on OpenSpec-enabled projects.
This project uses OpenSpec for spec-driven development. Specifications are the source of truth.
See @openspec/AGENTS.md for detailed conventions and guidelines.
<!-- OPENSPEC:END -->
```
#### Scenario: Creating new AGENTS.md
- **WHEN** AGENTS.md does not exist in the project root
- **THEN** create new file with OpenSpec content wrapped in markers using the same template as CLAUDE.md
#### Scenario: Updating existing CLAUDE.md
- **WHEN** CLAUDE.md already exists
- **THEN** preserve all existing content
- **AND** insert OpenSpec content at the beginning of the file using markers
- **AND** ensure markers don't duplicate if they already exist
#### Scenario: Updating existing AGENTS.md
- **WHEN** AGENTS.md already exists in the project root
- **THEN** preserve all existing content
- **AND** ensure the OpenSpec-managed block at the beginning of the file is refreshed without duplicating markers
#### Scenario: Managing content with markers
- **WHEN** using the marker system
- **THEN** use `<!-- OPENSPEC:START -->` to mark the beginning of managed content
- **AND** use `<!-- OPENSPEC:END -->` to mark the end of managed content
- **AND** allow OpenSpec to update its content without affecting user customizations
- **AND** preserve all content outside the markers intact
WHY use markers:
- Users may have existing CLAUDE.md or AGENTS.md instructions they want to keep
- OpenSpec can update its instructions in future versions
- Clear boundary between OpenSpec-managed and user-managed content
@@ -0,0 +1,41 @@
## MODIFIED Requirements
### Requirement: Update Behavior
The update command SHALL update OpenSpec instruction files to the latest templates in a team-friendly manner.
#### Scenario: Running update command
- **WHEN** a user runs `openspec update`
- **THEN** the command SHALL:
- Check if the `openspec` directory exists
- Replace `openspec/AGENTS.md` with the latest template (complete replacement)
- Create or refresh a root-level `AGENTS.md` file using the managed marker block (create if missing)
- Update **only existing** AI tool configuration files (e.g., CLAUDE.md)
- Check each registered AI tool configurator
- For each configurator, check if its file exists
- Update only files that already exist using their markers
- Preserve user content outside markers
- Display success message listing updated files
### Requirement: Tool-Agnostic Updates
The update command SHALL handle file updates in a predictable and safe manner while respecting team tool choices.
#### Scenario: Updating files
- **WHEN** updating files
- **THEN** completely replace `openspec/AGENTS.md` with the latest template
- **AND** create or update the root-level `AGENTS.md` using the OpenSpec markers
- **AND** update only the OpenSpec-managed blocks in **existing** AI tool files using markers
- **AND** use the default directory name `openspec`
- **AND** be idempotent (repeated runs have no additional effect)
- **AND** respect team members' AI tool choices by not creating additional tool files beyond the root `AGENTS.md`
### Requirement: Core Files Always Updated
The update command SHALL always update the core OpenSpec files and display an ASCII-safe success message.
#### Scenario: Successful update
- **WHEN** the update completes successfully
- **THEN** replace `openspec/AGENTS.md` with the latest template
- **AND** ensure the root-level `AGENTS.md` matches the latest template via the marker block
- **AND** update existing AI tool configuration files within markers
- **AND** display the message: "Updated OpenSpec instructions"
@@ -0,0 +1,17 @@
# Implementation Tasks
## 1. Extend Init Workflow
- [x] 1.1 Add an "AGENTS.md standard" option to the `openspec init` tool-selection prompt, respecting the existing UI conventions.
- [x] 1.2 Generate or refresh a root-level `AGENTS.md` file using the OpenSpec markers when that option is selected, sourcing content from the canonical template.
## 2. Enhance Update Command
- [x] 2.1 Ensure `openspec update` writes the root `AGENTS.md` from the latest template (creating it if missing) alongside `openspec/AGENTS.md`.
- [x] 2.2 Update success messaging and logging to reflect creation vs refresh of the AGENTS standard file.
## 3. Shared Template Handling
- [x] 3.1 Refactor template utilities if necessary so both commands reuse the same content without duplication.
- [x] 3.2 Add automated tests covering init/update flows for projects with and without an existing `AGENTS.md`, ensuring markers behave correctly.
## 4. Documentation
- [x] 4.1 Update CLI specs and user-facing docs to describe AGENTS standard support.
- [x] 4.2 Run `openspec validate add-agents-md-config --strict` and document any notable behavior changes.
@@ -0,0 +1,35 @@
# Allow Additional AI Tool Initialization After Setup
## Summary
- Let `openspec init` configure new AI coding tools for projects that already contain an OpenSpec structure.
- Keep the initialization flow safe by skipping structure creation and only generating files for tools the user explicitly selects.
- Provide clear feedback so users know which tool files were added versus already present.
## Motivation
Today `openspec init` exits with an error once an `openspec/` directory exists. That protects the directory layout, but it blocks
teams that start with one assistant (for example, Claude Code) and later want to add another such as Cursor. They have to create
those files by hand or rerun `init` in a clean clone, which undermines the "easy onboarding" promise. Letting the command extend
an existing installation keeps the workflow consistent and avoids manual file management.
## Proposal
1. Detect an existing OpenSpec structure at the start of `openspec init` and branch into an "extend" mode instead of exiting.
- Announce that the base structure already exists and that the command will only manage AI tool configuration files.
- Keep the existing guard for directories or files we must not overwrite.
2. Present the usual AI tool selection prompt even in extend mode, showing which tools are already configured.
- Skip disabled options that remain "coming soon".
- Mark already configured tools as such so users know whether selecting them will refresh or add files.
3. When the user selects additional tools, generate the same initialization files that a fresh run would create (e.g., Cursor
workspace files) while leaving untouched tools intact apart from marker-managed sections.
- Do nothing when the user selects no new tools and keep the previous error messaging to avoid silently succeeding.
4. Summarize the outcome (created, refreshed, skipped) before exiting with code 0 when work was performed.
- Include friendly guidance that future updates to shared content still come from `openspec update`.
## Out of Scope
- Changing how `openspec update` discovers or updates AI tool files.
- Supporting brand-new AI tools beyond those already wired into the CLI.
- Adding non-interactive flags for selecting multiple tools in one run (follow-up if needed).
## Risks & Mitigations
- **User confusion about extend mode** → Explicitly log what will happen before prompting and summarise results afterward.
- **Accidental overwrites** → Continue using marker-based updates and skip files unless the user chooses that tool.
- **Inconsistent state if init fails mid-run** → Reuse existing rollback/transaction logic so partial writes clean up.
@@ -0,0 +1,45 @@
## MODIFIED Requirements
### Requirement: Safety Checks
The command SHALL perform safety checks to prevent overwriting existing structures and ensure proper permissions.
#### Scenario: Detecting existing initialization
- **WHEN** the `openspec/` directory already exists
- **THEN** inform the user that OpenSpec is already initialized, skip recreating the base structure, and enter an extend mode
- **AND** continue to the AI tool selection step so additional tools can be configured
- **AND** display the existing-initialization error message only when the user declines to add any AI tools
### Requirement: Interactive Mode
The command SHALL provide an interactive menu for AI tool selection with clear navigation instructions.
#### Scenario: Displaying interactive menu
- **WHEN** run in fresh or extend mode
- **THEN** present a looping select menu that lets users toggle tools with Enter and finish via a "Done" option
- **AND** label already configured tools with "(already configured)" while keeping disabled options marked "coming soon"
- **AND** change the prompt copy in extend mode to "Which AI tools would you like to add or refresh?"
- **AND** display inline instructions clarifying that Enter toggles a tool and selecting "Done" confirms the list
## ADDED Requirements
### Requirement: Additional AI Tool Initialization
`openspec init` SHALL allow users to add configuration files for new AI coding assistants after the initial setup.
#### Scenario: Configuring an extra tool after initial setup
- **GIVEN** an `openspec/` directory already exists and at least one AI tool file is present
- **WHEN** the user runs `openspec init` and selects a different supported AI tool
- **THEN** generate that tool's configuration files with OpenSpec markers the same way as during first-time initialization
- **AND** leave existing tool configuration files unchanged except for managed sections that need refreshing
- **AND** exit with code 0 and display a success summary highlighting the newly added tool files
### Requirement: Success Output Enhancements
`openspec init` SHALL summarize tool actions when initialization or extend mode completes.
#### Scenario: Showing tool summary
- **WHEN** the command completes successfully
- **THEN** display a categorized summary of tools that were created, refreshed, or skipped (including already-configured skips)
- **AND** personalize the "Next steps" header using the names of the selected tools, defaulting to a generic label when none remain
### Requirement: Exit Code Adjustments
`openspec init` SHALL treat extend mode with no selected tools as a guarded error.
#### Scenario: Preventing empty extend runs
- **WHEN** OpenSpec is already initialized and the user selects no additional tools
- **THEN** exit with code 1 after showing the existing-initialization guidance message
@@ -0,0 +1,16 @@
# Implementation Tasks
## 1. Extend Init Guard
- [x] 1.1 Detect existing OpenSpec structures at the start of `openspec init` and enter an extend mode instead of failing.
- [x] 1.2 Log that core scaffolding will be skipped while still protecting against missing write permissions.
## 2. Update AI Tool Selection
- [x] 2.1 Present AI tool choices even in extend mode, indicating which tools are already configured.
- [x] 2.2 Ensure disabled "coming soon" tools remain non-selectable.
## 3. Generate Additional Tool Files
- [x] 3.1 Create configuration files for newly selected tools while leaving untouched tools unaffected apart from marker-managed sections.
- [x] 3.2 Summarize created, refreshed, and skipped tools before exiting with the appropriate code.
## 4. Verification
- [x] 4.1 Add tests covering rerunning `openspec init` to add another tool and the scenario where the user declines to add anything.
@@ -0,0 +1,119 @@
# Add Slash Command Support for Coding Agents
## Summary
- Enable OpenSpec to generate and update custom slash commands for supported coding agents (Claude Code and Cursor).
- Provide three slash commands aligned with OpenSpec's workflow: proposal (start a change proposal), apply (implement), and archive.
- Share slash command templating between agents to make future extensions simple.
## Motivation
Developers use different coding agents and editors. Having consistent slash commands across tools for the OpenSpec workflow reduces friction and ensures a standard way to trigger the workflow. Supporting both Claude Code and Cursor now lays a foundation for future agents that introduce slash command features.
## Proposal
1. During `openspec init`, when a user selects a supported tool, generate slash command configuration for three OpenSpec workflow stages:
- Claude (namespaced): `/openspec/proposal`, `/openspec/apply`, `/openspec/archive`.
- Cursor (flat, prefixed): `/openspec-proposal`, `/openspec-apply`, `/openspec-archive`.
- Semantics:
- Create – scaffold a change (ID, `proposal.md`, `tasks.md`, delta specs); validate strictly.
- Apply – implement an approved change; complete tasks; validate strictly.
- Archive – archive after deployment; update specs if needed.
- Each command file MUST embed concise, step-by-step instructions sourced from `openspec/README.md` (see Template Content section).
2. Store slash command files per tool:
- Claude Code: `.claude/commands/openspec/{proposal,apply,archive}.md`
- Cursor: `.cursor/commands/{openspec-proposal,openspec-apply,openspec-archive}.md`
- Ensure nested directories are created.
3. Command file format and metadata:
- Use Markdown with optional YAML frontmatter for tool metadata (name/title, description, category/tags) when supported by the tool.
- Place OpenSpec markers around the body only, never inside frontmatter.
- Keep the visible slash name, file name, and any frontmatter `name`/`id` consistently aligned (e.g., `proposal`, `openspec-proposal`).
- Namespacing: categorize these under “OpenSpec” and prefer unique IDs (e.g., `openspec-proposal`) to avoid collisions.
4. Centralize templates: define command bodies once and reuse across tools; apply minimal per-tool wrappers (frontmatter, categories, filenames).
5. During `openspec update`, refresh only existing slash command files (per-file basis) within markers; do not create missing files or new tools.
## Design Ideas
- Introduce `SlashCommandConfigurator` to manage multiple files per tool.
- Expose targets rather than a single `configFileName` (e.g., `getTargets(): Array<{ path: string; kind: 'slash'; id: string }>`).
- Provide `generateAll(projectPath, openspecDir)` for init and `updateExisting(projectPath, openspecDir)` for update.
- Per-tool adapters add only frontmatter and pathing; bodies come from shared templates.
- Templates live in `TemplateManager` with helpers that extract concise, authoritative snippets from `openspec/README.md`.
- Update flow logs per-file results so users see exactly which slash files were refreshed.
### Marker Placement
- Markers MUST wrap only the Markdown body contents:
- Frontmatter (if present) goes first.
- Then `<!-- OPENSPEC:START -->` … body … `<!-- OPENSPEC:END -->`.
- Avoid inserting markers into the YAML block to prevent parse errors.
### Idempotency and Creation Rules
- `init`: create all three files for the chosen tool(s) once; subsequent `init` runs are no-ops for existing files.
- `update`: refresh only files that exist; skip missing ones without creating new files.
- Directory creation for `.claude/commands/openspec/` and `.cursor/commands/` is the configurator’s responsibility.
### Command Naming & UX
- Claude Code: use namespacing in the slash itself for readability and grouping: `/openspec/proposal`, `/openspec/apply`, `/openspec/archive`.
- Cursor: use flat names with an `openspec-` prefix: `/openspec-proposal`, `/openspec-apply`, `/openspec-archive`. Group via `category: OpenSpec` when supported.
- Consistency: align file names, visible slash names, and any frontmatter `id` (e.g., `id: openspec-apply`).
- Migration: do not rename existing commands during `update`; apply new naming only on `init` (or via an explicit migrate step).
## Open Questions
- Validate exact metadata/frontmatter supported by each tool version; if unsupported, omit frontmatter and ship Markdown body only.
- Confirm the final Cursor command file location for the targeted versions; fall back to Markdown-only if Cursor does not parse frontmatter.
- Evaluate additional commands beyond the initial three (e.g., `/show-change`, `/validate-all`) based on user demand.
## Alternatives
- Hard-code slash command text per tool (rejected: duplicates content; increases maintenance).
- Delay Cursor support until its config stabilizes (partial accept): gate Cursor behind a feature flag until verified in real environments.
## Risks
- Tool configuration formats may change, requiring updates to wrappers/frontmatter.
- Incorrect paths or categories can hide commands; add path existence checks and clear logging.
- Marker misuse (inside frontmatter) can break parsing; enforce placement rules in tests.
## Future Work
- Support additional editors/agents that expose slash command APIs.
- Allow users to customize command names and categories during `openspec init`.
- Provide a dedicated command to regenerate slash commands without running full `update`.
## File Format Examples
The following examples illustrate expected structure. If a tool does not support frontmatter, omit the YAML block and keep only the markers + body.
### Claude Code: `.claude/commands/openspec/proposal.md`
```markdown
---
name: OpenSpec: Proposal
description: Scaffold a new OpenSpec change and validate strictly.
category: OpenSpec
tags: [openspec, change]
---
<!-- OPENSPEC:START -->
...command body from shared template...
<!-- OPENSPEC:END -->
```
Slash invocation: `/openspec/proposal` (namespaced)
### Cursor: `.cursor/commands/openspec-proposal.md`
```markdown
---
name: /openspec-proposal
id: openspec-proposal
category: OpenSpec
description: Scaffold a new OpenSpec change and validate strictly.
---
<!-- OPENSPEC:START -->
...command body from shared template...
<!-- OPENSPEC:END -->
```
Slash invocation: `/openspec-proposal` (flat, prefixed)
## Template Content
Templates should be brief, actionable, and sourced from `openspec/README.md` to avoid duplication. Each command body includes:
- Guardrails: ask 1–2 clarifying questions if needed; follow minimal-complexity rules; use `pnpm` for Node projects.
- Step list tailored to the workflow stage (proposal, apply, archive), including strict validation commands.
- Pointers to `openspec show`, `openspec list`, and troubleshooting tips when validation fails.
## Testing Strategy
- Golden snapshots for generated files per tool (frontmatter + markers + body).
- Partial presence tests: if 1–2 files exist, `update` only refreshes those and does not create missing ones.
- Marker placement tests: ensure markers never appear inside frontmatter; cover missing/duplicated marker recovery behavior.
- Logging tests: `update` reports per-file updates for slash commands.
@@ -0,0 +1,21 @@
## ADDED Requirements
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.
#### Scenario: Generating slash commands for Claude Code
- **WHEN** the user selects Claude Code during initialization
- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, and `.claude/commands/openspec/archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Cursor
- **WHEN** the user selects Cursor during initialization
- **THEN** create `.cursor/commands/openspec-proposal.md`, `.cursor/commands/openspec-apply.md`, and `.cursor/commands/openspec-archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for OpenCode
- **WHEN** the user selects OpenCode during initialization
- **THEN** create `.opencode/commands/openspec-proposal.md`, `.opencode/commands/openspec-apply.md`, and `.opencode/commands/openspec-archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
@@ -0,0 +1,22 @@
## ADDED Requirements
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones.
#### Scenario: Updating slash commands for Claude Code
- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, and `archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for Cursor
- **WHEN** `.cursor/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for OpenCode
- **WHEN** `.opencode/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Missing slash command file
- **WHEN** a tool lacks a slash command file
- **THEN** do not create a new file during update
@@ -0,0 +1,20 @@
# Implementation Tasks
## 1. Templates and Configurators
- [x] 1.1 Create shared templates for the Proposal, Apply, and Archive commands with instructions for each workflow stage from `openspec/README.md`.
- [x] 1.2 Implement a `SlashCommandConfigurator` base and tool-specific configurators for Claude Code and Cursor.
## 2. Claude Code Integration
- [x] 2.1 Generate `.claude/commands/openspec/{proposal,apply,archive}.md` during `openspec init` using shared templates.
- [x] 2.2 Update existing `.claude/commands/openspec/*` files during `openspec update`.
## 3. Cursor Integration
- [x] 3.1 Generate `.cursor/commands/{openspec-proposal,openspec-apply,openspec-archive}.md` during `openspec init` using shared templates.
- [x] 3.2 Update existing `.cursor/commands/*` files during `openspec update`.
## 4. Verification
- [x] 4.1 Add tests verifying slash command files are created and updated correctly.
## 5. OpenCode Integration
- [x] 5.1 Generate `.opencode/commands/{openspec-proposal,openspec-apply,openspec-archive}.md` during `openspec init` using shared templates.
- [x] 5.2 Update existing `.opencode/commands/*` files during `openspec update`.
@@ -0,0 +1,19 @@
## Why
Recent cross-shell regressions for `openspec` commands revealed that our existing unit/integration tests do not exercise the packaged CLI or shell-specific behavior. The prior attempt at Vitest spawn tests stalled because it coupled e2e coverage with `pnpm pack` installs, which fail in network-restricted environments. With those findings incorporated, we now need an approved plan to realign the work.
## What Changes
- Adopt a phased strategy that first stabilizes direct spawn testing of the built CLI (`node dist/cli/index.js`) using lightweight fixtures and a shared `runCLI` helper.
- Expand coverage once the spawn harness is stable, keeping the initial matrix focused on bash jobs for Linux/macOS and `pwsh` on Windows while exercising both the direct `node dist/cli/index.js` invocation and the bin shim with non-TTY defaults and captured diagnostics.
- Treat packaging/install validation as an optional CI safeguard: when a runner has registry access, run a simple pnpm-based pack→install→smoke-test flow; otherwise document it as out of scope while closing remaining hardening items.
- Close out the remaining cross-shell hardening items: ensure `.gitattributes` covers packaged assets, enforce executable bits for CLI shims during CI, and finish the pending SIGINT handling improvements.
## Impact
- Tests: add `test/cli-e2e` spawn suite, create the shared `runCLI` helper, and adjust `vitest.setup.ts` as needed.
- Tooling: update GitHub Actions workflows with the lightweight matrix above and (optionally) a packaging install check where network is available.
- Docs: note phase progress and any limitations inline in this proposal (or the relevant spec) so future phases have clear context.
### Phase 1 Status
- Shared `test/helpers/run-cli.ts` guarantees the CLI bundle exists before spawning and enforces non-TTY defaults for every invocation.
- New `test/cli-e2e/basic.test.ts` covers `--help`, `--version`, a successful `validate --all --json`, and an unknown-item error path against the `tmp-init` fixture copy.
- Legacy top-level `validate` exec tests now rely on `runCLI`, avoiding manual `execSync` usage while keeping their fixture authoring intact.
- CI matrix groundwork is in place (bash on Linux/macOS, pwsh on Windows) so the spawn suite runs the same way the helper does across supported shells.
@@ -0,0 +1,9 @@
## 1. Phase 1 – Stabilize Local Spawn Coverage
- [x] 1.1 Add `test/helpers/run-cli.ts` that ensures the build runs once and executes `node dist/cli/index.js` with non-TTY defaults; update `vitest.setup.ts` to reuse the shared build step.
- [x] 1.2 Seed `test/cli-e2e` using the minimal fixture set (`tmp-init` or copy) to cover help/version, a happy-path `validate`, and a representative error flow via the new helper.
- [x] 1.3 Migrate the highest-value existing CLI exec tests (e.g., validate) onto `runCLI` and summarize Phase 1 coverage in this proposal for the next phase.
## 2. Phase 2 – Expand Cross-Shell Validation
- [x] 2.1 Exercise both entry points (`node dist/cli/index.js`, `bin/openspec.js`) in the spawn suite and add diagnostics for shell/OS context.
- [x] 2.2 Extend GitHub Actions to run the spawn suite on bash jobs for Linux/macOS and a `pwsh` job on Windows; capture shell/OS diagnostics and note follow-ups for additional shells.
@@ -21,5 +21,5 @@
## 5. Optional (Not Needed Now)
- [x] 5.1 Add optional root param to discovery helpers (default process.cwd())
- [ ] 5.2 Consider threading root through command constructors if ever required
@@ -0,0 +1,13 @@
## Why
The current `openspec init` flow assumes a single assistant selection and stops once an OpenSpec structure already exists. That makes onboarding feel rigid: teams cannot configure multiple tools in one pass, they do not learn which files were refreshed, and the success copy always references Claude even when other assistants are involved.
## What Changes
- Allow selecting multiple assistants during `openspec init`, including refreshing existing configurations in a single run.
- Provide richer onboarding copy that summarizes which tool files were created or refreshed and guides users on next steps for each assistant.
- Align generated AI-instruction content and specs so CLAUDE.md and AGENTS.md share the same OpenSpec guidance.
- Update specs and tests to cover the multi-select prompt, improved summaries, and extend-mode coordination.
## Impact
- Specs: `cli-init`
- Code: `src/core/init.ts`, `src/core/config.ts`, `src/core/templates/*`, `src/core/configurators/*`
- Tests: `test/core/init.test.ts`, `test/core/update.test.ts`
@@ -0,0 +1,92 @@
## MODIFIED Requirements
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions based on user selection.
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** prompt the user with "Which AI tools do you use?" using a multi-select menu
- **AND** list every available tool with a checkbox:
- Claude Code (creates or refreshes CLAUDE.md and slash commands)
- Cursor (creates or refreshes `.cursor/commands/*` slash commands)
- AGENTS.md standard (creates or refreshes AGENTS.md with OpenSpec markers)
- **AND** show "(already configured)" beside tools whose managed files exist so users understand selections will refresh content
- **AND** treat disabled tools as "coming soon" and keep them unselectable
- **AND** allow confirming with Enter after selecting one or more tools
### Requirement: AI Tool Configuration Details
The command SHALL properly configure selected AI tools with OpenSpec-specific instructions using a marker system.
#### Scenario: Configuring Claude Code
- **WHEN** Claude Code is selected
- **THEN** create or update `CLAUDE.md` in the project root directory (not inside openspec/)
#### Scenario: Creating new CLAUDE.md
- **WHEN** CLAUDE.md does not exist
- **THEN** create new file with OpenSpec content wrapped in markers:
```markdown
<!-- OPENSPEC:START -->
# 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`
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: verb-led kebab-case (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, optional `design.md`, and spec deltas
- Validate with `openspec validate [change-id] --strict`
- Request approval before implementation
<!-- OPENSPEC:END -->
```
#### Scenario: Updating existing CLAUDE.md
- **WHEN** CLAUDE.md already exists
- **THEN** preserve all existing content
- **AND** insert OpenSpec content at the beginning of the file using markers
- **AND** ensure markers don't duplicate if they already exist
#### Scenario: Managing content with markers
- **WHEN** using the marker system
- **THEN** use `<!-- OPENSPEC:START -->` to mark the beginning of managed content
- **AND** use `<!-- OPENSPEC:END -->` to mark the end of managed content
- **AND** allow OpenSpec to update its content without affecting user customizations
- **AND** preserve all content outside the markers intact
### Requirement: Interactive Mode
The command SHALL provide an interactive menu for AI tool selection with clear navigation instructions.
#### Scenario: Displaying interactive menu
- **WHEN** run
- **THEN** prompt the user with: "Which AI tools do you use?"
- **AND** show a checkbox-based multi-select menu with available tools (Claude Code, Cursor, AGENTS.md standard)
- **AND** show disabled options as "coming soon" (not selectable)
- **AND** display inline help indicating Space toggles selections and Enter confirms
#### Scenario: Navigating the menu
- **WHEN** the user is in the menu
- **THEN** allow arrow keys to move between options
- **AND** allow Spacebar to toggle the highlighted option
- **AND** allow Enter key to confirm all current selections
### Requirement: Success Output
The command SHALL provide clear, actionable next steps upon successful initialization.
#### Scenario: Displaying success message
- **WHEN** initialization completes successfully
- **THEN** display a success banner followed by actionable prompts tailored to the selected tools
- **AND** summarize which assistant files were created versus refreshed (e.g., `CLAUDE.md (created)`, `.cursor/commands/openspec-apply.md (refreshed)`)
- **AND** include copy-pasteable onboarding prompts for each configured assistant, replacing placeholder text ([YOUR FEATURE HERE]) with real guidance to customize
- **AND** reference AGENTS.md-compatible assistants when no tool-specific file exists (e.g., when only AGENTS.md standard is selected)
@@ -0,0 +1,12 @@
## 1. Planning & Spec Updates
- [x] 1.1 Confirm overlap with `add-multi-agent-init` and coordinate extend-mode flow
- [x] 1.2 Update `openspec/specs/cli-init/spec.md` to capture multi-select onboarding requirements
## 2. Implementation
- [x] 2.1 Add multi-select support to the `openspec init` prompt, including indicators for existing tool configs
- [x] 2.2 Enhance success messaging to summarize created/refreshed assets per tool
- [x] 2.3 Ensure shared instruction template is applied consistently (CLAUDE.md, AGENTS.md, slash commands)
## 3. Quality
- [x] 3.1 Expand unit tests for init/update flows covering multi-select and summaries
- [x] 3.2 Perform `openspec init` smoke test in a temp directory (document output)
@@ -26,10 +26,6 @@
- Archive documentation
- Change proposals
## 6. Add Deprecation Notice (Optional Phase)
- [ ] Consider adding a deprecation warning before full removal
- [ ] Provide helpful message directing users to `openspec show` command
## 7. Testing
- [x] Ensure all tests pass after removal
- [x] Verify CLI help text no longer shows diff command
@@ -0,0 +1,25 @@
# Change: Sort Active Changes by Progress
## Problem
- The dashboard currently lists active changes in filesystem discovery order.
- Users cannot quickly spot proposals that have not started or are nearly complete.
- Inconsistent ordering between runs makes it harder to track progress when many changes exist.
## Proposal
1. Update the Active Changes list in the dashboard to sort by percentage of completion in ascending order so 0% items show first.
2. When two changes share the same completion percentage, break ties deterministically by change identifier (alphabetical).
## Benefits
- Highlights work that has not started yet, enabling quicker prioritization.
- Provides consistent ordering across machines and repeated runs.
- Keeps the dashboard compact while communicating the most important status signal.
## Risks & Mitigations
- **Risk:** Sorting logic could regress rendering when progress data is missing.
- **Mitigation:** Treat missing progress as 0% so items still surface and document behavior in tests.
- **Risk:** Additional sorting could impact performance for large change sets.
- **Mitigation:** The number of active changes is typically small; sorting a few entries is negligible.
## Success Criteria
- Dashboard output shows active changes ordered by ascending completion percentage with deterministic tie-breaking.
- Unit coverage verifying the sort when percentages vary and when ties occur.
@@ -0,0 +1,9 @@
## MODIFIED Requirements
### Requirement: Active Changes Display
The dashboard SHALL show active changes with visual progress indicators.
#### Scenario: Active changes ordered by completion percentage
- **WHEN** multiple active changes are displayed with progress information
- **THEN** list them sorted by completion percentage ascending so 0% items appear first
- **AND** treat missing progress values as 0% for ordering
- **AND** break ties by change identifier in ascending alphabetical order to keep output deterministic
@@ -0,0 +1,8 @@
# Implementation Tasks
## 1. Dashboard Sorting Logic
- [x] 1.1 Update the Active Changes rendering to sort by completion percentage ascending.
- [x] 1.2 Treat missing progress as 0% and break ties alphabetically by change identifier.
## 2. Verification
- [x] 2.1 Add tests that cover different completion percentages and tie cases to confirm deterministic ordering.
@@ -0,0 +1,29 @@
# Update Agent Instruction File Name
## Problem
The agent instructions live in `openspec/README.md`, which clashes with conventional project README usage and creates confusion for tooling and contributors.
## Solution
Rename the agent instruction file to `openspec/AGENTS.md` and update OpenSpec tooling to use the new filename:
- `openspec init` generates `AGENTS.md` instead of `README.md`
- Templates and code reference `AGENTS.md`
- Specifications and documentation are updated accordingly
## Benefits
- Clear separation from project documentation
- Consistent naming with other agent instruction files
- Simplifies tooling and project onboarding
## Implementation
- Rename instruction file and template
- Update CLI commands (`init`, `update`) to read/write `AGENTS.md`
- Adjust specs and documentation to reference the new path
## Risks
- Existing projects may still rely on `README.md`
- Tooling may miss lingering references to the old filename
## Success Metrics
- `openspec init` creates `openspec/AGENTS.md`
- `openspec update` refreshes `AGENTS.md`
- All specs reference `openspec/AGENTS.md`
@@ -0,0 +1,40 @@
## MODIFIED Requirements
### Requirement: Directory Creation
The command SHALL create the complete OpenSpec directory structure with all required directories and files.
#### Scenario: Creating OpenSpec structure
- **WHEN** `openspec init` is executed
- **THEN** create the following directory structure:
```
openspec/
├── project.md
├── AGENTS.md
├── specs/
└── changes/
└── archive/
```
### Requirement: File Generation
The command SHALL generate required template files with appropriate content for immediate use.
#### Scenario: Generating template files
- **WHEN** initializing OpenSpec
- **THEN** generate `AGENTS.md` containing complete OpenSpec instructions for AI assistants
- **AND** generate `project.md` with project context template
### Requirement: AI Tool Configuration Details
The command SHALL properly configure selected AI tools with OpenSpec-specific instructions using a marker system.
#### Scenario: Creating new CLAUDE.md
- **WHEN** CLAUDE.md does not exist
- **THEN** create new file with OpenSpec content wrapped in markers including reference to `@openspec/AGENTS.md`
### Requirement: Success Output
The command SHALL provide clear, actionable next steps upon successful initialization.
#### Scenario: Displaying success message
- **WHEN** initialization completes successfully
- **THEN** include prompt: "Please explain the OpenSpec workflow from openspec/AGENTS.md and how I should work with you on this project"
@@ -0,0 +1,22 @@
## MODIFIED Requirements
### Requirement: Update Behavior
The update command SHALL update OpenSpec instruction files to the latest templates in a team-friendly manner.
#### Scenario: Running update command
- **WHEN** a user runs `openspec update`
- **THEN** replace `openspec/AGENTS.md` with the latest template
### Requirement: File Handling
The update command SHALL handle file updates in a predictable and safe manner.
#### Scenario: Updating files
- **WHEN** updating files
- **THEN** completely replace `openspec/AGENTS.md` with the latest template
### Requirement: Core Files Always Updated
The update command SHALL always update the core OpenSpec files and display an ASCII-safe success message.
#### Scenario: Successful update
- **WHEN** the update completes successfully
- **THEN** replace `openspec/AGENTS.md` with the latest template
@@ -0,0 +1,27 @@
## MODIFIED Requirements
### Requirement: Project Structure
An OpenSpec project SHALL maintain a consistent directory structure for specifications and changes.
#### Scenario: Initializing project structure
- **WHEN** an OpenSpec project is initialized
- **THEN** it SHALL have this structure:
```
openspec/
├── project.md # Project-specific context
├── AGENTS.md # AI assistant instructions
├── specs/ # Current deployed capabilities
│ └── [capability]/ # Single, focused capability
│ ├── spec.md # WHAT and WHY
│ └── design.md # HOW (optional, for established patterns)
└── changes/ # Proposed changes
├── [change-name]/ # Descriptive change identifier
│ ├── proposal.md # Why, what, and impact
│ ├── tasks.md # Implementation checklist
│ ├── design.md # Technical decisions (optional)
│ └── specs/ # Complete future state
│ └── [capability]/
│ └── spec.md # Clean markdown (no diff syntax)
└── archive/ # Completed changes
└── YYYY-MM-DD-[name]/
```
@@ -0,0 +1,22 @@
# Update Agent Instruction File Name - Tasks
## 1. Rename Instruction File
- [x] Rename `openspec/README.md` to `openspec/AGENTS.md`
- [x] Update root references to new path
## 2. Update Templates
- [x] Rename `src/core/templates/readme-template.ts` to `agents-template.ts`
- [x] Update exported constant from `readmeTemplate` to `agentsTemplate`
## 3. Adjust CLI Commands
- [x] Modify `openspec init` to generate `AGENTS.md`
- [x] Update `openspec update` to refresh `AGENTS.md`
- [x] Ensure CLAUDE.md markers link to `@openspec/AGENTS.md`
## 4. Update Specifications
- [x] Modify `cli-init` spec to reference `AGENTS.md`
- [x] Modify `cli-update` spec to reference `AGENTS.md`
- [x] Modify `openspec-conventions` spec to include `AGENTS.md` in project structure
## 5. Validation
- [x] `pnpm test`
@@ -0,0 +1,19 @@
# Update Markdown Parser CRLF Handling
## Problem
Windows users report that `openspec validate` raises “Change must have a Why section” even when the section exists (see GitHub issue #77). The CLI currently splits markdown on `\n` and compares headers without stripping `\r`, so files saved with CRLF line endings keep a trailing carriage return in the header token. As a result the parser fails to detect `## Why`/`## What Changes`, triggering false validation errors and breaking the workflow on Windows-default editors.
## Solution
- Normalize markdown content inside the parser so CRLF and lone-CR inputs are treated as `\n` before section detection, trimming any carriage returns from titles and content comparisons.
- Reuse the normalized reader everywhere `MarkdownParser` is constructed to keep behavior consistent for validation, view, spec, and list flows.
- Add regression coverage that reproduces the failure (unit test around `parseChange` and a CLI spawn/e2e test that writes a CRLF change then runs `openspec validate`).
- Update the `cli-validate` spec to codify the expectation that required sections are recognized regardless of line-ending style.
## Benefits
- Restores correct validation behavior for Windows editors without requiring manual line-ending conversion.
- Locks in the fix with targeted tests so future parser refactors keep cross-platform support.
- Clarifies the spec so downstream work (e.g., cross-shell e2e plan) understands the non-negotiable behavior.
## Risks
- Low: parser normalization touches shared code paths that parse specs and changes; need to ensure no regressions in other command consumers (mitigated by existing parser tests plus the new CRLF fixtures).
@@ -0,0 +1,9 @@
## ADDED Requirements
### Requirement: Parser SHALL handle cross-platform line endings
The markdown parser SHALL correctly identify sections regardless of line ending format (LF, CRLF, CR).
#### Scenario: Required sections parsed with CRLF line endings
- **GIVEN** a change proposal markdown saved with CRLF line endings
- **AND** the document contains `## Why` and `## What Changes`
- **WHEN** running `openspec validate <change-id>`
- **THEN** validation SHALL recognize the sections and NOT raise parsing errors
@@ -0,0 +1,11 @@
## 1. Guard the regression
- [x] 1.1 Add a unit test that feeds a CRLF change document into `MarkdownParser.parseChange` and asserts `Why`/`What Changes` are detected.
- [x] 1.2 Add a CLI spawn/e2e test that writes a CRLF change, runs `openspec validate`, and expects success.
## 2. Normalize parsing
- [x] 2.1 Normalize line endings when constructing `MarkdownParser` so headers and content comparisons ignore `\r`.
- [x] 2.2 Ensure all CLI entry points (validate, view, spec conversion) reuse the normalized parser path.
## 3. Document and verify
- [x] 3.1 Update the `cli-validate` spec with a scenario covering CRLF line endings.
- [x] 3.2 Run the parser and CLI test suites (`pnpm test`, relevant spawn tests) to confirm the fix.
@@ -0,0 +1,12 @@
## Why
Validation errors like "no deltas found" or "missing requirement text" do not tell agents how to recover, leading to repeated failures. Making error output specific about headers, required text, and next actions will help assistants fix issues in a single pass.
## What Changes
- Extend `openspec validate` error reporting so each failure names the exact header, file, and expected structure, including concrete examples of compliant Markdown.
- Tailor messages for the most common mistakes (missing delta sections, absent descriptive requirement text, missing scenarios) with actionable fixes and suggested debug commands.
- Update docs/help output so the improved messaging is discoverable (e.g., `--help`, troubleshooting section).
- Add regression coverage to lock in the richer messaging for the top validation paths.
## Impact
- Affected specs: `specs/cli-validate`
- Affected code: `src/commands/validate.ts`, `src/core/validation`, `docs/`
@@ -0,0 +1,39 @@
## MODIFIED Requirements
### Requirement: Validation SHALL provide actionable remediation steps
Validation output SHALL include specific guidance to fix each error, including expected structure, example headers, and suggested commands to verify fixes.
#### Scenario: No deltas found in change
- **WHEN** validating a change with zero parsed deltas
- **THEN** show error "No deltas found" with guidance:
- Explain that change specs must include `## ADDED Requirements`, `## MODIFIED Requirements`, `## REMOVED Requirements`, or `## RENAMED Requirements`
- Remind authors that files must live under `openspec/changes/{id}/specs/<capability>/spec.md`
- Include an explicit note: "Spec delta files cannot start with titles before the operation headers"
- Suggest running `openspec change show {id} --json --deltas-only` for debugging
#### Scenario: Missing required sections
- **WHEN** a required section is missing
- **THEN** include expected header names and a minimal skeleton:
- For Spec: `## Purpose`, `## Requirements`
- For Change: `## Why`, `## What Changes`
- Provide an example snippet of the missing section with placeholder prose ready to copy
- Mention the quick-reference section in `openspec/AGENTS.md` as the authoritative template
#### Scenario: Missing requirement descriptive text
- **WHEN** a requirement header lacks descriptive text before scenarios
- **THEN** emit an error explaining that `### Requirement:` lines must be followed by narrative text before any `#### Scenario:` headers
- Show compliant example: "### Requirement: Foo" followed by "The system SHALL ..."
- Suggest adding 1-2 sentences describing the normative behavior prior to listing scenarios
- Reference the pre-validation checklist in `openspec/AGENTS.md`
### Requirement: Validator SHALL detect likely misformatted scenarios and warn with a fix
The validator SHALL recognize bulleted lines that look like scenarios (e.g., lines beginning with WHEN/THEN/AND) and emit a targeted warning with a conversion example to `#### Scenario:`.
#### Scenario: Bulleted WHEN/THEN under a Requirement
- **WHEN** bullets that start with WHEN/THEN/AND are found under a requirement without any `#### Scenario:` headers
- **THEN** emit warning: "Scenarios must use '#### Scenario:' headers", and show a conversion template:
```
#### Scenario: Short name
- **WHEN** ...
- **THEN** ...
- **AND** ...
```
@@ -0,0 +1,12 @@
## 1. Messaging enhancements
- [ ] 1.1 Inventory current validation failures and map each to the desired message improvements.
- [ ] 1.2 Implement structured error builders that include file paths, normalized header names, and example fixes.
- [ ] 1.3 Ensure `openspec validate --help` and troubleshooting docs mention the richer messages and debug tips.
## 2. Tests
- [ ] 2.1 Add unit tests for representative errors (no deltas, missing requirement body, missing scenarios) asserting the new wording.
- [ ] 2.2 Add integration coverage verifying the Next steps footer reflects contextual guidance.
## 3. Documentation
- [ ] 3.1 Update troubleshooting sections and CLI docs with sample output from the enhanced errors.
- [ ] 3.2 Note the change in CHANGELOG or release notes if applicable.
@@ -0,0 +1,12 @@
## Why
Agents fumble proposal formatting because the essential Markdown templates and formatting rules are buried mid-document. Reorganizing `openspec/AGENTS.md` with a prominent quick-reference and embedded examples will help assistants follow the process without guesswork.
## What Changes
- Restructure `openspec/AGENTS.md` so file formats and scaffold templates appear in a top-level quick-reference section before workflow prose.
- Embed copy/paste templates for `proposal.md`, `tasks.md`, `design.md`, and spec deltas alongside inline examples within the workflow steps.
- Add a pre-validation checklist that highlights the most common formatting pitfalls before running `openspec validate`.
- Split content into beginner vs. advanced sections to progressively disclose complexity while keeping advanced guidance accessible.
## Impact
- Affected specs: `specs/docs-agent-instructions`
- Affected code: `openspec/AGENTS.md`, `docs/`
@@ -0,0 +1,33 @@
## ADDED Requirements
### Requirement: Quick Reference Placement
The AI instructions SHALL begin with a quick-reference section that surfaces required file structures, templates, and formatting rules before any narrative guidance.
#### Scenario: Loading templates at the top
- **WHEN** `openspec/AGENTS.md` is regenerated or updated
- **THEN** the first substantive section after the title SHALL provide copy-ready headings for `proposal.md`, `tasks.md`, spec deltas, and scenario formatting
- **AND** link each template to the corresponding workflow step for deeper reading
### Requirement: Embedded Templates and Examples
`openspec/AGENTS.md` SHALL include complete copy/paste templates and inline examples exactly where agents make corresponding edits.
#### Scenario: Providing file templates
- **WHEN** authors reach the workflow guidance for drafting proposals and deltas
- **THEN** provide fenced Markdown templates that match the required structure (`## Why`, `## ADDED Requirements`, `#### Scenario:` etc.)
- **AND** accompany each template with a brief example showing correct header usage and scenario bullets
### Requirement: Pre-validation Checklist
`openspec/AGENTS.md` SHALL offer a concise pre-validation checklist that highlights common formatting mistakes before running `openspec validate`.
#### Scenario: Highlighting common validation failures
- **WHEN** a reader reaches the validation guidance
- **THEN** present a checklist reminding them to verify requirement headers, scenario formatting, and delta sections
- **AND** include reminders about at least `#### Scenario:` usage and descriptive requirement text before scenarios
### Requirement: Progressive Disclosure of Workflow Guidance
The documentation SHALL separate beginner essentials from advanced topics so newcomers can focus on core steps without losing access to advanced workflows.
#### Scenario: Organizing beginner and advanced sections
- **WHEN** reorganizing `openspec/AGENTS.md`
- **THEN** keep an introductory section limited to the minimum steps (scaffold, draft, validate, request review)
- **AND** move advanced topics (multi-capability changes, archiving details, tooling deep dives) into clearly labeled later sections
- **AND** provide anchor links from the quick-reference to those advanced sections
@@ -0,0 +1,11 @@
## 1. Instruction redesign
- [ ] 1.1 Draft a quick-reference section that surfaces file templates and formatting rules at the top of `openspec/AGENTS.md`.
- [ ] 1.2 Reorganize the workflow narrative with inline examples and progressive disclosure for advanced topics.
## 2. Templates and checklists
- [ ] 2.1 Add copy/paste templates for proposal, tasks, design, and spec delta files.
- [ ] 2.2 Insert a pre-validation checklist capturing common lint failures before running `openspec validate`.
## 3. Documentation updates
- [ ] 3.1 Update supporting docs or README pointers so contributors find the redesigned instructions.
- [ ] 3.2 Confirm examples and references stay in sync with the new scaffold command guidance.
@@ -0,0 +1,12 @@
## Why
Validation currently errors on changes without spec deltas, even when the change is intentionally proposal-only or tooling-only. This creates false negatives and noisy CI.
## What Changes
- Make change validation scope-aware: validate only artifacts that exist.
- Only error on "No deltas found" if spec delta files exist but parse to zero deltas.
- Keep archive stricter: if specs exist but parse to zero deltas, fail; allow `--skip-specs` for tooling-only changes.
## Impact
- Affected specs: cli-validate
- Affected code: `src/commands/validate.ts`, `src/core/validation/validator.ts`
@@ -0,0 +1,25 @@
## ADDED Requirements
### Requirement: Scope-Aware Change Validation
The validator SHALL validate only artifacts that exist for a change, avoiding errors for proposal-only or tooling-only changes.
#### Scenario: Proposal-only change
- **WHEN** a change contains `proposal.md` but has no `specs/` directory or contains no `*/spec.md` files
- **THEN** validate the proposal (Why/What sections)
- **AND** do not require or validate spec deltas
#### Scenario: Delta validation when specs exist
- **WHEN** a change contains one or more `specs/<capability>/spec.md` files
- **THEN** validate delta-formatted specs with existing rules (SHALL/MUST, scenarios, duplicates, conflicts)
## MODIFIED Requirements
### Requirement: Validation SHALL provide actionable remediation steps
Validation output SHALL include specific guidance to fix each error, including expected structure, example headers, and suggested commands to verify fixes.
#### Scenario: No deltas found in change
- **WHEN** validating a change that contains `specs/` with one or more `*/spec.md` files but the parser finds zero deltas
- **THEN** show error "No deltas found" with guidance:
- Ensure `openspec/changes/{id}/specs/` has `.md` files that include delta headers
- Use delta headers: `## ADDED Requirements`, `## MODIFIED Requirements`, `## REMOVED Requirements`, `## RENAMED Requirements`
- Each requirement must include at least one `#### Scenario:` block
- Try: `openspec change show {id} --json --deltas-only` to inspect parsed deltas
@@ -0,0 +1,16 @@
## 1. Validator changes
- [ ] 1.1 Change `validateChangeDeltaSpecs` to only emit "Change must have at least one delta" when `specs/` exists and contains at least one `*/spec.md` but parsed total deltas is 0
- [ ] 1.2 Return valid (no error) when `specs/` directory is missing or has no `spec.md` files
## 2. CLI changes
- [ ] 2.1 In bulk validation, keep current behavior (call delta validator). Behavior remains correct after 1.1
- [ ] 2.2 Add a short INFO log in human-readable mode when a change has no `specs/` (optional)
## 3. Documentation
- [ ] 3.1 Update README and template: "Validation checks only existing artifacts. Proposal-only changes are valid without spec deltas."
## 4. Tests
- [ ] 4.1 Add test: proposal-only change passes validation without deltas
- [ ] 4.2 Add test: specs present but zero parsed deltas → ERROR
- [ ] 4.3 Add test: specs present with proper deltas → valid
@@ -0,0 +1,13 @@
## Why
The project root currently receives a full copy of the OpenSpec agent instructions, duplicating the content that also lives in `openspec/AGENTS.md`. When teams edit one copy but not the other, the files drift and onboarding assistants see conflicting guidance.
## What Changes
- Keep generating the complete template in `openspec/AGENTS.md` during `openspec init` and follow-up updates.
- Replace the root-level file (`AGENTS.md` or `CLAUDE.md`, depending on tool selection) with a short hand-off that explains the project uses OpenSpec and points directly to `openspec/AGENTS.md`.
- Add a dedicated stub template so both the init and update flows reuse the same minimal copy instructions.
- Update CLI tests and documentation to reflect the new root-level messaging and ensure the OpenSpec marker block still protects future updates.
## Impact
- Affected specs: `cli-init`, `cli-update`
- Affected code: `src/core/init.ts`, `src/core/update.ts`, `src/core/templates/agents-template.ts`
- Update assets/readmes that mention the root `AGENTS.md` contents to reference the new stub message.
@@ -0,0 +1,15 @@
## 1. Templates
- [x] 1.1 Add a shared stub template that renders the root agent instructions hand-off message.
- [x] 1.2 Ensure the stub covers both `AGENTS.md` and `CLAUDE.md` variants.
## 2. Init Flow
- [x] 2.1 Update `createInitArtifacts` to write the stub to the project root instead of the full instructions.
- [x] 2.2 Preserve the managed block markers so future updates can overwrite the stub safely.
## 3. Update Flow
- [x] 3.1 Make the update command refresh the root stub rather than the full instructions.
- [x] 3.2 Confirm the update log output still reflects the files that changed.
## 4. Tests & Docs
- [x] 4.1 Adjust CLI/init tests to match the new root content.
- [x] 4.2 Document the stub message in `openspec/specs/cli-init` and `openspec/specs/cli-update` (and any relevant README snippets).
@@ -0,0 +1,15 @@
## Why
OpenSpec currently creates the root-level `AGENTS.md` stub only when teams explicitly select the "AGENTS.md standard" tool during `openspec init`. Projects that skip that checkbox never get a managed stub, so non-native assistants (Copilot, Codeium, etc.) have no entry point and later `openspec update` runs silently create the file without any context. We need to bake the stub into initialization, clarify the tool selection experience, and keep the update workflow aligned so every teammate lands on the right instructions from day one.
## What Changes
- Update `openspec init` so the root `AGENTS.md` stub is always generated (first run and extend mode) and refreshed from a shared utility instead of being tied to a tool selection.
- Redesign the AI tool selection wizard to split options into "Natively supported" (Claude, Cursor, OpenCode, …) and an informational "Other tools" section that explains the always-on `AGENTS.md` hand-off.
- Adjust CLI specs, prompts, and success messaging to reflect the new categories while keeping extend-mode behaviour consistent.
- Update automated tests and fixtures to cover the unconditional stub creation and the reworked prompt flow.
- Refresh documentation and onboarding snippets so they no longer describe the stub as opt-in and instead call out the new grouping.
- Ensure `openspec update` continues to reconcile both `openspec/AGENTS.md` and the root stub, documenting the expected behaviour so mismatched setups self-heal.
## Impact
- Affected specs: `cli-init`, `cli-update`
- Affected code: `src/core/init.ts`, `src/core/config.ts`, `src/core/configurators/agents.ts`, `src/core/templates/agents-root-stub.ts`, `src/core/update.ts`, related tests under `test/core/`
- Docs & assets: README, CHANGELOG, any setup guides that reference choosing the "AGENTS.md standard" option
@@ -0,0 +1,32 @@
## MODIFIED Requirements
### Requirement: AI Tool Configuration
The command SHALL configure AI coding assistants with OpenSpec instructions using a grouped selection experience so teams can enable native integrations while always provisioning guidance for other assistants.
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** present a multi-select wizard that separates options into two headings:
- **Natively supported providers** shows each available first-party integration (Claude Code, Cursor, OpenCode, …) with checkboxes
- **Other tools** explains that the root-level `AGENTS.md` stub is always generated for AGENTS-compatible assistants and cannot be deselected
- **AND** mark already configured native tools with "(already configured)" to signal that choosing them will refresh managed content
- **AND** keep disabled or unavailable providers labelled as "coming soon" so users know they cannot opt in yet
- **AND** allow confirming the selection even when no native provider is chosen because the root stub remains enabled by default
- **AND** change the base prompt copy in extend mode to "Which natively supported AI tools would you like to add or refresh?"
### Requirement: Exit Code Adjustments
`openspec init` SHALL treat extend mode without new native tool selections as a successful refresh.
#### Scenario: Allowing empty extend runs
- **WHEN** OpenSpec is already initialized and the user selects no additional natively supported tools
- **THEN** complete successfully while refreshing the root `AGENTS.md` stub
- **AND** exit with code 0
## ADDED Requirements
### Requirement: Root instruction stub
`openspec init` SHALL always scaffold the root-level `AGENTS.md` hand-off so every teammate finds the primary OpenSpec instructions.
#### Scenario: Creating root `AGENTS.md`
- **GIVEN** the project may or may not already contain an `AGENTS.md` file
- **WHEN** initialization completes in fresh or extend mode
- **THEN** create or refresh `AGENTS.md` at the repository root using the managed marker block from `TemplateManager.getAgentsStandardTemplate()`
- **AND** preserve any existing content outside the managed markers while replacing the stub text inside them
- **AND** create the stub regardless of which native AI tools are selected
@@ -0,0 +1,10 @@
## MODIFIED Requirements
### Requirement: Tool-Agnostic Updates
The update command SHALL refresh OpenSpec-managed files in a predictable manner while respecting each team's chosen tooling.
#### Scenario: Updating files
- **WHEN** updating files
- **THEN** completely replace `openspec/AGENTS.md` with the latest template
- **AND** create or refresh the root-level `AGENTS.md` stub using the managed marker block, even if the file was previously absent
- **AND** update only the OpenSpec-managed sections inside existing AI tool files, leaving user-authored content untouched
- **AND** avoid creating new native-tool configuration files (slash commands, CLAUDE.md, etc.) unless they already exist
@@ -0,0 +1,11 @@
## 1. Implementation
- [ ] 1.1 Refactor `openspec init` to always generate the root `AGENTS.md` stub (initial run and extend mode) via shared helper logic.
- [ ] 1.2 Rework the AI tool selection wizard to surface "Natively supported" vs "Other tools" groupings and make the stub non-optional.
- [ ] 1.3 Update CLI messaging, templates, and configurators so the new flow stays in sync across init and update commands.
- [ ] 1.4 Refresh unit/integration tests to cover the unconditional stub and the regrouped prompt layout.
- [ ] 1.5 Update documentation, README snippets, and CHANGELOG entries that mention the opt-in `AGENTS.md` experience.
## 2. Validation
- [ ] 2.1 Run `pnpm test` targeting CLI init/update suites.
- [ ] 2.2 Execute `openspec validate update-cli-init-root-agents --strict`.
- [ ] 2.3 Perform a manual smoke test: run `openspec init` in a temp directory, confirm stub + grouped prompts, rerun in extend mode.
@@ -0,0 +1,21 @@
## Why
Today's flow requires maintainers to merge the changeset PR, cut a tag, and draft the GitHub release by hand. Once the release is published on GitHub our existing automation pushes the package to npm, so the manual work centers on shepherding the release PR, updating version metadata, and keeping GitHub releases in sync with the changelog. Those repeated human steps slow the cycle time and increase the risk that the release/tag documentation falls out of sync with what was actually published to npm.
## What Changes
- Adopt the [changesets/action](https://github.com/changesets/action) workflow so pushes to `main` either open/update a release PR or, when the release PR merge lands, run our publish script automatically with the provided `NPM_TOKEN` and `GITHUB_TOKEN`.【5e2b47†L1-L43】【b9dad5†L13-L37】
- Add a `release` npm script that builds the project and calls `changeset publish`, allowing the action to handle version bumps, changelog commits, and npm publishing in one command.【b9dad5†L13-L21】
- Enable `createGithubReleases` in the action configuration so the workflow drafts GitHub releases from the published packages immediately after publishing.【5e2b47†L21-L38】
- Document the automated release flow, required repository secrets, and how maintainers should handle failed publishes or hotfixes.
## How the Maintainer Flow Changes
| Step | Current process | Future process |
| --- | --- | --- |
| Prepare release | Merge changeset PR, wait for GitHub release automation, then manually draft release notes and tags | Merge changeset PR, the action updates/merges the release PR and handles version bumps automatically |
| Publish npm package | Happens automatically after the GitHub release is published | Continues to happen automatically via `changeset publish`; no manual npm work |
| GitHub release | Maintainers draft releases manually and ensure notes match the changelog | Action creates GitHub releases immediately after publishing, based on changeset information |
| Clean up docs/process | Maintainers follow separate docs describing manual tagging and release steps | Update docs to remove manual tag/release steps and document the automated workflow |
## Impact
- Affected automation: `.github/workflows/release.yml`
- Affected package metadata: `package.json` scripts
- Documentation: `README.md` or `/docs` release instructions
@@ -0,0 +1,12 @@
## 1. Release workflow automation
- [ ] 1.1 Add a `.github/workflows/release.yml` that runs on pushes to `main`, sets up pnpm + Node 20, installs dependencies, and invokes `changesets/action@v1` with `publish: pnpm run release`.
- [ ] 1.2 Configure the action with `createGithubReleases: true` and document required secrets (`NPM_TOKEN`, default `GITHUB_TOKEN`) plus recommended concurrency safeguards.
- [ ] 1.3 Validate the workflow using `act` or a dry-run push to confirm the action opens release PRs when changesets exist and publishes when the release PR merge lands.
## 2. Package release script
- [ ] 2.1 Add a `release` script to `package.json` that builds the project and runs `changeset publish` using pnpm.
- [ ] 2.2 Ensure the script respects the existing `prepare`/`prepublishOnly` hooks to avoid duplicate builds and update documentation or scripts if adjustments are needed.
## 3. Documentation and recovery steps
- [ ] 3.1 Update maintainer docs (e.g., README or `/docs`) with the end-to-end automated release flow, explicitly removing the manual tag/release steps that are no longer required and explaining how changesets drive the release PR.
- [ ] 3.2 Document fallback steps for failed publishes (rerun workflow, manual publish) and the hotfix path when a release must be cut without pending changesets.
+73 -92
View File
@@ -3,9 +3,7 @@
## Purpose
The `openspec init` command SHALL create a complete OpenSpec directory structure in any project, enabling immediate adoption of OpenSpec conventions with support for multiple AI coding assistants.
## Requirements
### Requirement: Progress Indicators
The command SHALL display progress indicators during initialization to provide clear feedback about each step.
@@ -21,30 +19,26 @@ The command SHALL display progress indicators during initialization to provide c
- Then success: "✔ AI tools configured"
### Requirement: Directory Creation
The command SHALL create the complete OpenSpec directory structure with all required directories and files.
#### Scenario: Creating OpenSpec structure
- **WHEN** `openspec init` is executed
- **THEN** create the following directory structure:
```
openspec/
├── project.md
├── README.md
├── AGENTS.md
├── specs/
└── changes/
└── archive/
```
### Requirement: File Generation
The command SHALL generate required template files with appropriate content for immediate use.
#### Scenario: Generating template files
- **WHEN** initializing OpenSpec
- **THEN** generate `README.md` containing complete OpenSpec instructions for AI assistants
- **THEN** generate `openspec/AGENTS.md` containing complete OpenSpec instructions for AI assistants
- **AND** generate `project.md` with project context template
### Requirement: AI Tool Configuration
@@ -54,10 +48,14 @@ The command SHALL configure AI coding assistants with OpenSpec instructions base
#### Scenario: Prompting for AI tool selection
- **WHEN** run interactively
- **THEN** prompt user to select AI tools to configure:
- Claude Code (updates/creates CLAUDE.md with OpenSpec markers)
- Cursor (future)
- Aider (future)
- **THEN** prompt the user with "Which AI tools do you use?" using a multi-select menu
- **AND** list every available tool with a checkbox:
- Claude Code (creates or refreshes CLAUDE.md and slash commands)
- Cursor (creates or refreshes `.cursor/commands/*` slash commands)
- AGENTS.md standard (creates or refreshes AGENTS.md stub with OpenSpec markers)
- **AND** show "(already configured)" beside tools whose managed files exist so users understand selections will refresh content
- **AND** treat disabled tools as "coming soon" and keep them unselectable
- **AND** allow confirming with Enter after selecting one or more tools
### Requirement: AI Tool Configuration Details
@@ -67,112 +65,49 @@ The command SHALL properly configure selected AI tools with OpenSpec-specific in
- **WHEN** Claude Code is selected
- **THEN** create or update `CLAUDE.md` in the project root directory (not inside openspec/)
- **AND** populate the managed block with a short stub that points teammates to `@/openspec/AGENTS.md`
#### Scenario: Creating new CLAUDE.md
- **WHEN** CLAUDE.md does not exist
- **THEN** create new file with OpenSpec content wrapped in markers:
- **THEN** create new file with stub instructions wrapped in markers so the full workflow stays in `openspec/AGENTS.md`:
```markdown
<!-- OPENSPEC:START -->
# OpenSpec Project
# OpenSpec Instructions
This document provides instructions for AI coding assistants on how to use OpenSpec conventions for spec-driven development. Follow these rules precisely when working on OpenSpec-enabled projects.
This project uses OpenSpec to manage AI assistant workflows.
This project uses OpenSpec for spec-driven development. Specifications are the source of truth.
See @openspec/README.md for detailed conventions and guidelines.
- Full guidance lives in '@/openspec/AGENTS.md'.
- Keep this managed block so 'openspec update' can refresh the instructions.
<!-- OPENSPEC:END -->
```
#### Scenario: Updating existing CLAUDE.md
- **WHEN** CLAUDE.md already exists
- **THEN** preserve all existing content
- **AND** insert OpenSpec content at the beginning of the file using markers
- **AND** ensure markers don't duplicate if they already exist
#### Scenario: Managing content with markers
- **WHEN** using the marker system
- **THEN** use `<!-- OPENSPEC:START -->` to mark the beginning of managed content
- **AND** use `<!-- OPENSPEC:END -->` to mark the end of managed content
- **AND** allow OpenSpec to update its content without affecting user customizations
- **AND** preserve all content outside the markers intact
WHY use markers:
- Users may have existing CLAUDE.md instructions they want to keep
- OpenSpec can update its instructions in future versions
- Clear boundary between OpenSpec-managed and user-managed content
### Requirement: Interactive Mode
The command SHALL provide an interactive menu for AI tool selection with clear navigation instructions.
#### Scenario: Displaying interactive menu
- **WHEN** run
- **THEN** prompt user with: "Which AI tool do you use?"
- **AND** show single-select menu with available tools:
- Claude Code
- **AND** show disabled options as "coming soon" (not selectable):
- Cursor (coming soon)
- Aider (coming soon)
- Continue (coming soon)
#### Scenario: Navigating the menu
- **WHEN** user is in the menu
- **THEN** allow arrow keys to move between options
- **AND** allow Enter key to select the highlighted option
- **WHEN** run in fresh or extend mode
- **THEN** present a looping select menu that lets users toggle tools with Enter and finish via a "Done" option
- **AND** label already configured tools with "(already configured)" while keeping disabled options marked "coming soon"
- **AND** change the prompt copy in extend mode to "Which AI tools would you like to add or refresh?"
- **AND** display inline instructions clarifying that Enter toggles a tool and selecting "Done" confirms the list
### Requirement: Safety Checks
The command SHALL perform safety checks to prevent overwriting existing structures and ensure proper permissions.
#### Scenario: Detecting existing initialization
- **WHEN** `openspec/` directory already exists
- **THEN** display error with ora fail indicator:
- "✖ Error: OpenSpec seems to already be initialized. Use 'openspec update' to update the structure."
#### Scenario: Checking write permissions
- **WHEN** checking initialization feasibility
- **THEN** verify write permissions in the target directory silently
- **AND** only display error if permissions are insufficient
- **WHEN** the `openspec/` directory already exists
- **THEN** inform the user that OpenSpec is already initialized, skip recreating the base structure, and enter an extend mode
- **AND** continue to the AI tool selection step so additional tools can be configured
- **AND** display the existing-initialization error message only when the user declines to add any AI tools
### Requirement: Success Output
The command SHALL provide clear, actionable next steps upon successful initialization.
#### Scenario: Displaying success message
- **WHEN** initialization completes successfully
- **THEN** display actionable prompts for AI-driven workflow:
```
✔ OpenSpec initialized successfully!
Next steps - Copy these prompts to Claude:
────────────────────────────────────────────────────────────
1. Populate your project context:
"Please read openspec/project.md and help me fill it out
with details about my project, tech stack, and conventions"
2. Create your first change proposal:
"I want to add [YOUR FEATURE HERE]. Please create an
OpenSpec change proposal for this feature"
3. Learn the OpenSpec workflow:
"Please explain the OpenSpec workflow from openspec/README.md
and how I should work with you on this project"
────────────────────────────────────────────────────────────
```
The prompts SHALL:
- Be copy-pasteable for immediate use with AI tools
- Guide users through the AI-driven workflow
- Replace placeholder text ([YOUR FEATURE HERE]) with actual features
- **THEN** include prompt: "Please explain the OpenSpec workflow from openspec/AGENTS.md and how I should work with you on this project"
### Requirement: Exit Codes
@@ -187,10 +122,56 @@ The command SHALL use consistent exit codes to indicate different failure modes.
- 2: Insufficient permissions (reserved for future use)
- 3: User cancelled operation (reserved for future use)
### Requirement: Additional AI Tool Initialization
`openspec init` SHALL allow users to add configuration files for new AI coding assistants after the initial setup.
#### Scenario: Configuring an extra tool after initial setup
- **GIVEN** an `openspec/` directory already exists and at least one AI tool file is present
- **WHEN** the user runs `openspec init` and selects a different supported AI tool
- **THEN** generate that tool's configuration files with OpenSpec markers the same way as during first-time initialization
- **AND** leave existing tool configuration files unchanged except for managed sections that need refreshing
- **AND** exit with code 0 and display a success summary highlighting the newly added tool files
### Requirement: Success Output Enhancements
`openspec init` SHALL summarize tool actions when initialization or extend mode completes.
#### Scenario: Showing tool summary
- **WHEN** the command completes successfully
- **THEN** display a categorized summary of tools that were created, refreshed, or skipped (including already-configured skips)
- **AND** personalize the "Next steps" header using the names of the selected tools, defaulting to a generic label when none remain
### Requirement: Exit Code Adjustments
`openspec init` SHALL treat extend mode with no selected tools as a guarded error.
#### Scenario: Preventing empty extend runs
- **WHEN** OpenSpec is already initialized and the user selects no additional tools
- **THEN** exit with code 1 after showing the existing-initialization guidance message
### Requirement: Slash Command Configuration
The init command SHALL generate slash command files for supported editors using shared templates.
#### Scenario: Generating slash commands for Claude Code
- **WHEN** the user selects Claude Code during initialization
- **THEN** create `.claude/commands/openspec/proposal.md`, `.claude/commands/openspec/apply.md`, and `.claude/commands/openspec/archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for Cursor
- **WHEN** the user selects Cursor during initialization
- **THEN** create `.cursor/commands/openspec-proposal.md`, `.cursor/commands/openspec-apply.md`, and `.cursor/commands/openspec-archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
#### Scenario: Generating slash commands for OpenCode
- **WHEN** the user selects OpenCode during initialization
- **THEN** create `.opencode/commands/openspec-proposal.md`, `.opencode/commands/openspec-apply.md`, and `.opencode/commands/openspec-archive.md`
- **AND** populate each file from shared templates so command text matches other tools
- **AND** each template includes instructions for the relevant OpenSpec workflow stage
## Why
Manual creation of OpenSpec structure is error-prone and creates adoption friction. A standardized init command ensures:
- Consistent structure across all projects
- Proper AI instruction files are always included
- Quick onboarding for new projects
- Clear conventions from the start
- Clear conventions from the start
+39 -32
View File
@@ -5,22 +5,12 @@
As a developer using OpenSpec, I want to update the OpenSpec instructions in my project when new versions are released, so that I can benefit from improvements to AI agent instructions.
## Requirements
### Requirement: Update Behavior
The update command SHALL update OpenSpec instruction files to the latest templates in a team-friendly manner.
#### Scenario: Running update command
- **WHEN** a user runs `openspec update`
- **THEN** the command SHALL:
- Check if the `openspec` directory exists
- Replace `openspec/README.md` with the latest template (complete replacement)
- Update **only existing** AI tool configuration files (e.g., CLAUDE.md)
- Check each registered AI tool configurator
- For each configurator, check if its file exists
- Update only files that already exist using their markers
- Preserve user content outside markers
- **Never create new AI tool configuration files**
- Display success message listing updated files
- **THEN** replace `openspec/AGENTS.md` with the latest template
- **AND** if a root-level stub (`AGENTS.md`/`CLAUDE.md`) exists, refresh it so it points to `@/openspec/AGENTS.md`
### Requirement: Prerequisites
@@ -34,39 +24,56 @@ The command SHALL require an existing OpenSpec structure before allowing updates
- **AND** exit with code 1
### Requirement: File Handling
The update command SHALL handle file updates in a predictable and safe manner.
#### Scenario: Updating files
- **WHEN** updating files
- **THEN** completely replace `openspec/AGENTS.md` with the latest template
- **AND** if a root-level stub exists, update the managed block content so it keeps directing teammates to `@/openspec/AGENTS.md`
### Requirement: Tool-Agnostic Updates
The update command SHALL handle file updates in a predictable and safe manner while respecting team tool choices.
#### Scenario: Updating files
- **WHEN** updating files
- **THEN** completely replace `openspec/README.md` with the latest template
- **THEN** completely replace `openspec/AGENTS.md` with the latest template
- **AND** update the root-level `AGENTS.md` using the OpenSpec markers only when that file already exists, keeping the stub content that links to `@/openspec/AGENTS.md`
- **AND** update only the OpenSpec-managed blocks in **existing** AI tool files using markers
- **AND** use the default directory name `openspec`
- **AND** be idempotent (repeated runs have no additional effect)
- **AND** respect team members' AI tool choices by not creating unwanted files
### Requirement: Tool-Agnostic Updates
The update command SHALL update only existing AI tool configuration files and SHALL NOT create new ones.
#### Scenario: Updating existing tool files
- **WHEN** a user runs `openspec update`
- **THEN** update each AI tool configuration file that exists (e.g., CLAUDE.md, COPILOT.md)
- **AND** do not create missing tool configuration files
- **AND** preserve user content outside OpenSpec markers
- **AND** respect team members' AI tool choices by not creating additional tool files beyond the root `AGENTS.md`
- **AND** do not create new root-level stub files when none are present
### Requirement: Core Files Always Updated
The update command SHALL always update the core OpenSpec files and display an ASCII-safe success message.
#### Scenario: Successful update
- **WHEN** the update completes successfully
- **THEN** replace `openspec/README.md` with the latest template
- **AND** update existing AI tool configuration files within markers
- **AND** display the message: "Updated OpenSpec instructions"
- **THEN** replace `openspec/AGENTS.md` with the latest template
- **AND** if a root-level stub exists, refresh it so it still directs contributors to `@/openspec/AGENTS.md`
### Requirement: Slash Command Updates
The update command SHALL refresh existing slash command files for configured tools without creating new ones.
#### Scenario: Updating slash commands for Claude Code
- **WHEN** `.claude/commands/openspec/` contains `proposal.md`, `apply.md`, and `archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for Cursor
- **WHEN** `.cursor/commands/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Updating slash commands for OpenCode
- **WHEN** `.opencode/command/` contains `openspec-proposal.md`, `openspec-apply.md`, and `openspec-archive.md`
- **THEN** refresh each file using shared templates
- **AND** ensure templates include instructions for the relevant workflow stage
#### Scenario: Missing slash command file
- **WHEN** a tool lacks a slash command file
- **THEN** do not create a new file during update
## Edge Cases
@@ -101,4 +108,4 @@ Users SHALL be able to:
The update process SHALL be:
- Simple and fast (no version checking)
- Predictable (same result every time)
- Self-contained (no network required)
- Self-contained (no network required)
+9
View File
@@ -199,3 +199,12 @@ The validate command SHALL handle ambiguous names and explicit type overrides to
- **THEN** the CLI SHALL not display interactive prompts
- **AND** SHALL print non-interactive hints or chosen outputs as appropriate
### Requirement: Parser SHALL handle cross-platform line endings
The markdown parser SHALL correctly identify sections regardless of line ending format (LF, CRLF, CR).
#### Scenario: Required sections parsed with CRLF line endings
- **GIVEN** a change proposal markdown saved with CRLF line endings
- **AND** the document contains `## Why` and `## What Changes`
- **WHEN** running `openspec validate <change-id>`
- **THEN** validation SHALL recognize the sections and NOT raise parsing errors
+5 -12
View File
@@ -36,20 +36,13 @@ The dashboard SHALL display a summary section with key project metrics.
- **THEN** summary shows zero counts for all metrics
### Requirement: Active Changes Display
The dashboard SHALL show active changes with visual progress indicators.
#### Scenario: Active changes with progress bars
- **WHEN** there are in-progress changes with tasks
- **THEN** system displays each change with change name left-aligned
- **AND** visual progress bar using Unicode characters
- **AND** percentage completion on the right
#### Scenario: No active changes
- **WHEN** all changes are completed or no changes exist
- **THEN** active changes section is omitted from display
#### Scenario: Active changes ordered by completion percentage
- **WHEN** multiple active changes are displayed with progress information
- **THEN** list them sorted by completion percentage ascending so 0% items appear first
- **AND** treat missing progress values as 0% for ordering
- **AND** break ties by change identifier in ascending alphabetical order to keep output deterministic
### Requirement: Completed Changes Display
+2 -4
View File
@@ -14,17 +14,15 @@ OpenSpec conventions SHALL mandate a structured spec format with clear requireme
- **THEN** authors SHALL use `### Requirement: ...` followed by at least one `#### Scenario: ...` section
### Requirement: Project Structure
An OpenSpec project SHALL maintain a consistent directory structure for specifications and changes.
#### Scenario: Initializing project structure
- **WHEN** an OpenSpec project is initialized
- **THEN** it SHALL have this structure:
```
openspec/
├── project.md # Project-specific context
├── README.md # AI assistant instructions
├── AGENTS.md # AI assistant instructions
├── specs/ # Current deployed capabilities
│ └── [capability]/ # Single, focused capability
│ ├── spec.md # WHAT and WHY
@@ -245,7 +243,7 @@ An OpenSpec project SHALL maintain a consistent directory structure for specific
```
openspec/
├── project.md # Project-specific context
├── README.md # AI assistant instructions
├── AGENTS.md # AI assistant instructions
├── specs/ # Current deployed capabilities
│ └── [capability]/ # Single, focused capability
│ ├── spec.md # WHAT and WHY
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@fission-ai/openspec",
"version": "0.1.0",
"version": "0.7.0",
"description": "AI-native system for spec-driven development",
"keywords": [
"openspec",
@@ -18,8 +18,7 @@
"author": "OpenSpec Contributors",
"type": "module",
"publishConfig": {
"access": "public",
"tag": "next"
"access": "public"
},
"exports": {
".": {
@@ -60,6 +59,7 @@
"vitest": "^3.2.4"
},
"dependencies": {
"@inquirer/core": "^10.2.2",
"@inquirer/prompts": "^7.8.0",
"chalk": "^5.5.0",
"commander": "^14.0.0",
+23 -14
View File
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
'@inquirer/core':
specifier: ^10.2.2
version: 10.2.2(@types/node@24.2.0)
'@inquirer/prompts':
specifier: ^7.8.0
version: 7.8.0(@types/node@24.2.0)
@@ -257,6 +260,10 @@ packages:
cpu: [x64]
os: [win32]
'@inquirer/ansi@1.0.0':
resolution: {integrity: sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==}
engines: {node: '>=18'}
'@inquirer/checkbox@4.2.0':
resolution: {integrity: sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==}
engines: {node: '>=18'}
@@ -275,8 +282,8 @@ packages:
'@types/node':
optional: true
'@inquirer/core@10.1.15':
resolution: {integrity: sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==}
'@inquirer/core@10.2.2':
resolution: {integrity: sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==}
engines: {node: '>=18'}
peerDependencies:
'@types/node': '>=18'
@@ -1440,9 +1447,11 @@ snapshots:
'@esbuild/win32-x64@0.25.8':
optional: true
'@inquirer/ansi@1.0.0': {}
'@inquirer/checkbox@4.2.0(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@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)
ansi-escapes: 4.3.2
@@ -1452,16 +1461,16 @@ snapshots:
'@inquirer/confirm@5.1.14(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@types/node@24.2.0)
optionalDependencies:
'@types/node': 24.2.0
'@inquirer/core@10.1.15(@types/node@24.2.0)':
'@inquirer/core@10.2.2(@types/node@24.2.0)':
dependencies:
'@inquirer/ansi': 1.0.0
'@inquirer/figures': 1.0.13
'@inquirer/type': 3.0.8(@types/node@24.2.0)
ansi-escapes: 4.3.2
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
@@ -1472,7 +1481,7 @@ snapshots:
'@inquirer/editor@4.2.15(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@types/node@24.2.0)
external-editor: 3.1.0
optionalDependencies:
@@ -1480,7 +1489,7 @@ snapshots:
'@inquirer/expand@4.0.17(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@types/node@24.2.0)
yoctocolors-cjs: 2.1.2
optionalDependencies:
@@ -1497,21 +1506,21 @@ snapshots:
'@inquirer/input@4.2.1(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@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.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@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.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@types/node@24.2.0)
ansi-escapes: 4.3.2
optionalDependencies:
@@ -1534,7 +1543,7 @@ snapshots:
'@inquirer/rawlist@4.1.5(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@inquirer/core': 10.2.2(@types/node@24.2.0)
'@inquirer/type': 3.0.8(@types/node@24.2.0)
yoctocolors-cjs: 2.1.2
optionalDependencies:
@@ -1542,7 +1551,7 @@ snapshots:
'@inquirer/search@3.1.0(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@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)
yoctocolors-cjs: 2.1.2
@@ -1551,7 +1560,7 @@ snapshots:
'@inquirer/select@4.3.1(@types/node@24.2.0)':
dependencies:
'@inquirer/core': 10.1.15(@types/node@24.2.0)
'@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)
ansi-escapes: 4.3.2
+18 -10
View File
@@ -1,17 +1,25 @@
export const OPENSPEC_DIR_NAME = 'openspec';
export interface OpenSpecConfig {
aiTools: string[];
}
export const OPENSPEC_MARKERS = {
start: '<!-- OPENSPEC:START -->',
end: '<!-- OPENSPEC:END -->'
};
export const AI_TOOLS = [
{ name: 'Claude Code', value: 'claude', available: true },
{ name: 'Cursor', value: 'cursor', available: false },
{ name: 'Aider', value: 'aider', available: false },
{ name: 'Continue', value: 'continue', available: false }
];
export interface OpenSpecConfig {
aiTools: string[];
}
export interface AIToolOption {
name: string;
value: string;
available: boolean;
successLabel?: string;
}
export const AI_TOOLS: AIToolOption[] = [
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code' },
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor' },
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode' },
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code' },
{ name: 'AGENTS.md (works with Codex, Amp, VS Code, GitHub Copilot, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
];
+23
View File
@@ -0,0 +1,23 @@
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
);
}
}
+4 -1
View File
@@ -1,13 +1,16 @@
import { ToolConfigurator } from './base.js';
import { ClaudeConfigurator } from './claude.js';
import { AgentsStandardConfigurator } from './agents.js';
export class ToolRegistry {
private static tools: Map<string, ToolConfigurator> = new Map();
static {
const claudeConfigurator = new ClaudeConfigurator();
const agentsConfigurator = new AgentsStandardConfigurator();
// Register with the ID that matches the checkbox value
this.tools.set('claude', claudeConfigurator);
this.tools.set('agents', agentsConfigurator);
}
static register(tool: ToolConfigurator): void {
@@ -25,4 +28,4 @@ export class ToolRegistry {
static getAvailable(): ToolConfigurator[] {
return this.getAll().filter(tool => tool.isAvailable);
}
}
}
+85
View File
@@ -0,0 +1,85 @@
import path from 'path';
import { FileSystemUtils } from '../../../utils/file-system.js';
import { TemplateManager, SlashCommandId } from '../../templates/index.js';
import { OPENSPEC_MARKERS } from '../../config.js';
export interface SlashCommandTarget {
id: SlashCommandId;
path: string;
kind: 'slash';
}
const ALL_COMMANDS: SlashCommandId[] = ['proposal', 'apply', 'archive'];
export abstract class SlashCommandConfigurator {
abstract readonly toolId: string;
abstract readonly isAvailable: boolean;
getTargets(): SlashCommandTarget[] {
return ALL_COMMANDS.map((id) => ({
id,
path: this.getRelativePath(id),
kind: 'slash'
}));
}
async generateAll(projectPath: string, _openspecDir: string): Promise<string[]> {
const createdOrUpdated: string[] = [];
for (const target of this.getTargets()) {
const body = TemplateManager.getSlashCommandBody(target.id).trim();
const filePath = path.join(projectPath, target.path);
if (await FileSystemUtils.fileExists(filePath)) {
await this.updateBody(filePath, body);
} else {
const frontmatter = this.getFrontmatter(target.id);
const sections: string[] = [];
if (frontmatter) {
sections.push(frontmatter.trim());
}
sections.push(`${OPENSPEC_MARKERS.start}\n${body}\n${OPENSPEC_MARKERS.end}`);
const content = sections.join('\n') + '\n';
await FileSystemUtils.writeFile(filePath, content);
}
createdOrUpdated.push(target.path);
}
return createdOrUpdated;
}
async updateExisting(projectPath: string, _openspecDir: string): Promise<string[]> {
const updated: string[] = [];
for (const target of this.getTargets()) {
const filePath = path.join(projectPath, target.path);
if (await FileSystemUtils.fileExists(filePath)) {
const body = TemplateManager.getSlashCommandBody(target.id).trim();
await this.updateBody(filePath, body);
updated.push(target.path);
}
}
return updated;
}
protected abstract getRelativePath(id: SlashCommandId): string;
protected abstract getFrontmatter(id: SlashCommandId): string | undefined;
private async updateBody(filePath: string, body: string): Promise<void> {
const content = await FileSystemUtils.readFile(filePath);
const startIndex = content.indexOf(OPENSPEC_MARKERS.start);
const endIndex = content.indexOf(OPENSPEC_MARKERS.end);
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
throw new Error(`Missing OpenSpec markers in ${filePath}`);
}
const before = content.slice(0, startIndex + OPENSPEC_MARKERS.start.length);
const after = content.slice(endIndex);
const updatedContent = `${before}\n${body}\n${after}`;
await FileSystemUtils.writeFile(filePath, updatedContent);
}
}
+42
View File
@@ -0,0 +1,42 @@
import { SlashCommandConfigurator } from './base.js';
import { SlashCommandId } from '../../templates/index.js';
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: '.claude/commands/openspec/proposal.md',
apply: '.claude/commands/openspec/apply.md',
archive: '.claude/commands/openspec/archive.md'
};
const FRONTMATTER: Record<SlashCommandId, string> = {
proposal: `---
name: OpenSpec: Proposal
description: Scaffold a new OpenSpec change and validate strictly.
category: OpenSpec
tags: [openspec, change]
---`,
apply: `---
name: OpenSpec: Apply
description: Implement an approved OpenSpec change and keep tasks in sync.
category: OpenSpec
tags: [openspec, apply]
---`,
archive: `---
name: OpenSpec: Archive
description: Archive a deployed OpenSpec change and update specs.
category: OpenSpec
tags: [openspec, archive]
---`
};
export class ClaudeSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = 'claude';
readonly isAvailable = true;
protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}
protected getFrontmatter(id: SlashCommandId): string {
return FRONTMATTER[id];
}
}
+42
View File
@@ -0,0 +1,42 @@
import { SlashCommandConfigurator } from './base.js';
import { SlashCommandId } from '../../templates/index.js';
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: '.cursor/commands/openspec-proposal.md',
apply: '.cursor/commands/openspec-apply.md',
archive: '.cursor/commands/openspec-archive.md'
};
const FRONTMATTER: Record<SlashCommandId, string> = {
proposal: `---
name: /openspec-proposal
id: openspec-proposal
category: OpenSpec
description: Scaffold a new OpenSpec change and validate strictly.
---`,
apply: `---
name: /openspec-apply
id: openspec-apply
category: OpenSpec
description: Implement an approved OpenSpec change and keep tasks in sync.
---`,
archive: `---
name: /openspec-archive
id: openspec-archive
category: OpenSpec
description: Archive a deployed OpenSpec change and update specs.
---`
};
export class CursorSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = 'cursor';
readonly isAvailable = true;
protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}
protected getFrontmatter(id: SlashCommandId): string {
return FRONTMATTER[id];
}
}
+21
View File
@@ -0,0 +1,21 @@
import { SlashCommandConfigurator } from "./base.js";
import { SlashCommandId } from "../../templates/index.js";
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: ".kilocode/workflows/openspec-proposal.md",
apply: ".kilocode/workflows/openspec-apply.md",
archive: ".kilocode/workflows/openspec-archive.md"
};
export class KiloCodeSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = "kilocode";
readonly isAvailable = true;
protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}
protected getFrontmatter(_id: SlashCommandId): string | undefined {
return undefined;
}
}
+41
View File
@@ -0,0 +1,41 @@
import { SlashCommandConfigurator } from "./base.js";
import { SlashCommandId } from "../../templates/index.js";
const FILE_PATHS: Record<SlashCommandId, string> = {
proposal: ".opencode/command/openspec-proposal.md",
apply: ".opencode/command/openspec-apply.md",
archive: ".opencode/command/openspec-archive.md",
};
const FRONTMATTER: Record<SlashCommandId, string> = {
proposal: `---
agent: build
description: Scaffold a new OpenSpec change and validate strictly.
---
The user has requested the following change proposal. Use the openspec instructions to create their change proposal.
<UserRequest>
$ARGUMENTS
</UserRequest>
`,
apply: `---
agent: build
description: Implement an approved OpenSpec change and keep tasks in sync.
---`,
archive: `---
agent: build
description: Archive a deployed OpenSpec change and update specs.
---`,
};
export class OpenCodeSlashCommandConfigurator extends SlashCommandConfigurator {
readonly toolId = "opencode";
readonly isAvailable = true;
protected getRelativePath(id: SlashCommandId): string {
return FILE_PATHS[id];
}
protected getFrontmatter(id: SlashCommandId): string | undefined {
return FRONTMATTER[id];
}
}
+33
View File
@@ -0,0 +1,33 @@
import { SlashCommandConfigurator } from './base.js';
import { ClaudeSlashCommandConfigurator } from './claude.js';
import { CursorSlashCommandConfigurator } from './cursor.js';
import { KiloCodeSlashCommandConfigurator } from './kilocode.js';
import { OpenCodeSlashCommandConfigurator } from './opencode.js';
export class SlashCommandRegistry {
private static configurators: Map<string, SlashCommandConfigurator> = new Map();
static {
const claude = new ClaudeSlashCommandConfigurator();
const cursor = new CursorSlashCommandConfigurator();
const kilocode = new KiloCodeSlashCommandConfigurator();
const opencode = new OpenCodeSlashCommandConfigurator();
this.configurators.set(claude.toolId, claude);
this.configurators.set(cursor.toolId, cursor);
this.configurators.set(kilocode.toolId, kilocode);
this.configurators.set(opencode.toolId, opencode);
}
static register(configurator: SlashCommandConfigurator): void {
this.configurators.set(configurator.toolId, configurator);
}
static get(toolId: string): SlashCommandConfigurator | undefined {
return this.configurators.get(toolId);
}
static getAll(): SlashCommandConfigurator[] {
return Array.from(this.configurators.values());
}
}
+6 -4
View File
@@ -43,7 +43,8 @@ export class JsonConverter {
}
private extractNameFromPath(filePath: string): string {
const parts = filePath.split('/');
const normalizedPath = filePath.replaceAll('\\', '/');
const parts = normalizedPath.split('/');
for (let i = parts.length - 1; i >= 0; i--) {
if (parts[i] === 'specs' || parts[i] === 'changes') {
@@ -53,7 +54,8 @@ export class JsonConverter {
}
}
const fileName = parts[parts.length - 1];
return fileName.replace('.md', '');
const fileName = parts[parts.length - 1] ?? '';
const dotIndex = fileName.lastIndexOf('.');
return dotIndex > 0 ? fileName.slice(0, dotIndex) : fileName;
}
}
}
+744 -73
View File
@@ -1,72 +1,573 @@
import path from 'path';
import { select } from '@inquirer/prompts';
import {
createPrompt,
isBackspaceKey,
isDownKey,
isEnterKey,
isSpaceKey,
isUpKey,
useKeypress,
usePagination,
useState,
} from '@inquirer/core';
import chalk from 'chalk';
import ora from 'ora';
import { FileSystemUtils } from '../utils/file-system.js';
import { TemplateManager, ProjectContext } from './templates/index.js';
import { ToolRegistry } from './configurators/registry.js';
import { OpenSpecConfig, AI_TOOLS, OPENSPEC_DIR_NAME } from './config.js';
import { SlashCommandRegistry } from './configurators/slash/registry.js';
import {
OpenSpecConfig,
AI_TOOLS,
OPENSPEC_DIR_NAME,
AIToolOption,
} from './config.js';
import { PALETTE } from './styles/palette.js';
const PROGRESS_SPINNER = {
interval: 80,
frames: ['░░░', '▒░░', '▒▒░', '▒▒▒', '▓▒▒', '▓▓▒', '▓▓▓', '▒▓▓', '░▒▓'],
};
const LETTER_MAP: Record<string, string[]> = {
O: [' ████ ', '██ ██', '██ ██', '██ ██', ' ████ '],
P: ['█████ ', '██ ██', '█████ ', '██ ', '██ '],
E: ['██████', '██ ', '█████ ', '██ ', '██████'],
N: ['██ ██', '███ ██', '██ ███', '██ ██', '██ ██'],
S: [' █████', '██ ', ' ████ ', ' ██', '█████ '],
C: [' █████', '██ ', '██ ', '██ ', ' █████'],
' ': [' ', ' ', ' ', ' ', ' '],
};
type ToolLabel = {
primary: string;
annotation?: string;
};
const sanitizeToolLabel = (raw: string): string =>
raw.replace(/✅/gu, '✔').trim();
const parseToolLabel = (raw: string): ToolLabel => {
const sanitized = sanitizeToolLabel(raw);
const match = sanitized.match(/^(.*?)\s*\((.+)\)$/u);
if (!match) {
return { primary: sanitized };
}
return {
primary: match[1].trim(),
annotation: match[2].trim(),
};
};
const isSelectableChoice = (
choice: ToolWizardChoice
): choice is Extract<ToolWizardChoice, { selectable: true }> => choice.selectable;
type ToolWizardChoice =
| {
kind: 'heading' | 'info';
value: string;
label: ToolLabel;
selectable: false;
}
| {
kind: 'option';
value: string;
label: ToolLabel;
configured: boolean;
selectable: true;
};
type ToolWizardConfig = {
extendMode: boolean;
baseMessage: string;
choices: ToolWizardChoice[];
initialSelected?: string[];
};
type WizardStep = 'intro' | 'select' | 'review';
type ToolSelectionPrompt = (config: ToolWizardConfig) => Promise<string[]>;
type RootStubStatus = 'created' | 'updated' | 'skipped';
const ROOT_STUB_CHOICE_VALUE = '__root_stub__';
const OTHER_TOOLS_HEADING_VALUE = '__heading-other__';
const LIST_SPACER_VALUE = '__list-spacer__';
const toolSelectionWizard = createPrompt<string[], ToolWizardConfig>(
(config, done) => {
const totalSteps = 3;
const [step, setStep] = useState<WizardStep>('intro');
const selectableChoices = config.choices.filter(isSelectableChoice);
const initialCursorIndex = config.choices.findIndex((choice) =>
choice.selectable
);
const [cursor, setCursor] = useState<number>(
initialCursorIndex === -1 ? 0 : initialCursorIndex
);
const [selected, setSelected] = useState<string[]>(() => {
const initial = new Set(
(config.initialSelected ?? []).filter((value) =>
selectableChoices.some((choice) => choice.value === value)
)
);
return selectableChoices
.map((choice) => choice.value)
.filter((value) => initial.has(value));
});
const [error, setError] = useState<string | null>(null);
const selectedSet = new Set(selected);
const pageSize = Math.max(config.choices.length, 1);
const updateSelected = (next: Set<string>) => {
const ordered = selectableChoices
.map((choice) => choice.value)
.filter((value) => next.has(value));
setSelected(ordered);
};
const page = usePagination({
items: config.choices,
active: cursor,
pageSize,
loop: false,
renderItem: ({ item, isActive }) => {
if (!item.selectable) {
const prefix = item.kind === 'info' ? ' ' : '';
const textColor =
item.kind === 'heading' ? PALETTE.lightGray : PALETTE.midGray;
return `${PALETTE.midGray(' ')} ${PALETTE.midGray(' ')} ${textColor(
`${prefix}${item.label.primary}`
)}`;
}
const isSelected = selectedSet.has(item.value);
const cursorSymbol = isActive
? PALETTE.white('›')
: PALETTE.midGray(' ');
const indicator = isSelected
? PALETTE.white('◉')
: PALETTE.midGray('○');
const nameColor = isActive ? PALETTE.white : PALETTE.midGray;
const annotation = item.label.annotation
? PALETTE.midGray(` (${item.label.annotation})`)
: '';
const configuredNote = item.configured
? PALETTE.midGray(' (already configured)')
: '';
const label = `${nameColor(item.label.primary)}${annotation}${configuredNote}`;
return `${cursorSymbol} ${indicator} ${label}`;
},
});
const moveCursor = (direction: 1 | -1) => {
if (selectableChoices.length === 0) {
return;
}
let nextIndex = cursor;
while (true) {
nextIndex = nextIndex + direction;
if (nextIndex < 0 || nextIndex >= config.choices.length) {
return;
}
if (config.choices[nextIndex]?.selectable) {
setCursor(nextIndex);
return;
}
}
};
useKeypress((key) => {
if (step === 'intro') {
if (isEnterKey(key)) {
setStep('select');
}
return;
}
if (step === 'select') {
if (isUpKey(key)) {
moveCursor(-1);
setError(null);
return;
}
if (isDownKey(key)) {
moveCursor(1);
setError(null);
return;
}
if (isSpaceKey(key)) {
const current = config.choices[cursor];
if (!current || !current.selectable) return;
const next = new Set(selected);
if (next.has(current.value)) {
next.delete(current.value);
} else {
next.add(current.value);
}
updateSelected(next);
setError(null);
return;
}
if (isEnterKey(key)) {
setStep('review');
setError(null);
return;
}
if (key.name === 'escape') {
const next = new Set<string>();
updateSelected(next);
setError(null);
}
return;
}
if (step === 'review') {
if (isEnterKey(key)) {
const finalSelection = config.choices
.map((choice) => choice.value)
.filter(
(value) =>
selectedSet.has(value) && value !== ROOT_STUB_CHOICE_VALUE
);
done(finalSelection);
return;
}
if (isBackspaceKey(key) || key.name === 'escape') {
setStep('select');
setError(null);
}
}
});
const rootStubChoice = selectableChoices.find(
(choice) => choice.value === ROOT_STUB_CHOICE_VALUE
);
const rootStubSelected = rootStubChoice
? selectedSet.has(ROOT_STUB_CHOICE_VALUE)
: false;
const nativeChoices = selectableChoices.filter(
(choice) => choice.value !== ROOT_STUB_CHOICE_VALUE
);
const selectedNativeChoices = nativeChoices.filter((choice) =>
selectedSet.has(choice.value)
);
const formatSummaryLabel = (
choice: Extract<ToolWizardChoice, { selectable: true }>
) => {
const annotation = choice.label.annotation
? PALETTE.midGray(` (${choice.label.annotation})`)
: '';
const configuredNote = choice.configured
? PALETTE.midGray(' (already configured)')
: '';
return `${PALETTE.white(choice.label.primary)}${annotation}${configuredNote}`;
};
const stepIndex = step === 'intro' ? 1 : step === 'select' ? 2 : 3;
const lines: string[] = [];
lines.push(PALETTE.midGray(`Step ${stepIndex}/${totalSteps}`));
lines.push('');
if (step === 'intro') {
const introHeadline = config.extendMode
? 'Extend your OpenSpec tooling'
: 'Configure your OpenSpec tooling';
const introBody = config.extendMode
? 'We detected an existing setup. We will help you refresh or add integrations.'
: "Let's get your AI assistants connected so they understand OpenSpec.";
lines.push(PALETTE.white(introHeadline));
lines.push(PALETTE.midGray(introBody));
lines.push('');
lines.push(PALETTE.midGray('Press Enter to continue.'));
} else if (step === 'select') {
lines.push(PALETTE.white(config.baseMessage));
lines.push(
PALETTE.midGray(
'Use ↑/↓ to move · Space to toggle · Enter to review selections.'
)
);
lines.push('');
lines.push(page);
lines.push('');
lines.push(PALETTE.midGray('Selected configuration:'));
if (rootStubSelected && rootStubChoice) {
lines.push(
` ${PALETTE.white('-')} ${formatSummaryLabel(rootStubChoice)}`
);
}
if (selectedNativeChoices.length === 0) {
lines.push(
` ${PALETTE.midGray('- No natively supported providers selected')}`
);
} else {
selectedNativeChoices.forEach((choice) => {
lines.push(
` ${PALETTE.white('-')} ${formatSummaryLabel(choice)}`
);
});
}
} else {
lines.push(PALETTE.white('Review selections'));
lines.push(
PALETTE.midGray('Press Enter to confirm or Backspace to adjust.')
);
lines.push('');
if (rootStubSelected && rootStubChoice) {
lines.push(
`${PALETTE.white('▌')} ${formatSummaryLabel(rootStubChoice)}`
);
}
if (selectedNativeChoices.length === 0) {
lines.push(
PALETTE.midGray(
'No natively supported providers selected. Universal instructions will still be applied.'
)
);
} else {
selectedNativeChoices.forEach((choice) => {
lines.push(
`${PALETTE.white('▌')} ${formatSummaryLabel(choice)}`
);
});
}
}
if (error) {
return [lines.join('\n'), chalk.red(error)];
}
return lines.join('\n');
}
);
type InitCommandOptions = {
prompt?: ToolSelectionPrompt;
};
export class InitCommand {
private readonly prompt: ToolSelectionPrompt;
constructor(options: InitCommandOptions = {}) {
this.prompt = options.prompt ?? ((config) => toolSelectionWizard(config));
}
async execute(targetPath: string): Promise<void> {
const projectPath = path.resolve(targetPath);
const openspecDir = OPENSPEC_DIR_NAME;
const openspecPath = path.join(projectPath, openspecDir);
// Validation happens silently in the background
await this.validate(projectPath, openspecPath);
const extendMode = await this.validate(projectPath, openspecPath);
const existingToolStates = await this.getExistingToolStates(projectPath);
this.renderBanner(extendMode);
// Get configuration (after validation to avoid prompts if validation fails)
const config = await this.getConfiguration();
const config = await this.getConfiguration(existingToolStates, extendMode);
const availableTools = AI_TOOLS.filter((tool) => tool.available);
const selectedIds = new Set(config.aiTools);
const selectedTools = availableTools.filter((tool) =>
selectedIds.has(tool.value)
);
const created = selectedTools.filter(
(tool) => !existingToolStates[tool.value]
);
const refreshed = selectedTools.filter(
(tool) => existingToolStates[tool.value]
);
const skippedExisting = availableTools.filter(
(tool) => !selectedIds.has(tool.value) && existingToolStates[tool.value]
);
const skipped = availableTools.filter(
(tool) => !selectedIds.has(tool.value) && !existingToolStates[tool.value]
);
// Step 1: Create directory structure
const structureSpinner = ora({ text: 'Creating OpenSpec structure...', stream: process.stdout }).start();
await this.createDirectoryStructure(openspecPath);
await this.generateFiles(openspecPath, config);
structureSpinner.succeed('OpenSpec structure created');
// Step 2: Configure AI tools
const toolSpinner = ora({ text: 'Configuring AI tools...', stream: process.stdout }).start();
await this.configureAITools(projectPath, openspecDir, config.aiTools);
toolSpinner.succeed('AI tools configured');
// Success message
this.displaySuccessMessage(openspecDir, config);
}
private async validate(projectPath: string, openspecPath: string): Promise<void> {
// Check if OpenSpec already exists
if (await FileSystemUtils.directoryExists(openspecPath)) {
throw new Error(
`OpenSpec seems to already be initialized at ${openspecPath}.\n` +
`Use 'openspec update' to update the structure.`
if (!extendMode) {
const structureSpinner = this.startSpinner(
'Creating OpenSpec structure...'
);
await this.createDirectoryStructure(openspecPath);
await this.generateFiles(openspecPath, config);
structureSpinner.stopAndPersist({
symbol: PALETTE.white('▌'),
text: PALETTE.white('OpenSpec structure created'),
});
} else {
ora({ stream: process.stdout }).info(
PALETTE.midGray(
'ℹ OpenSpec already initialized. Skipping base scaffolding.'
)
);
}
// Check write permissions
if (!await FileSystemUtils.ensureWritePermissions(projectPath)) {
throw new Error(`Insufficient permissions to write to ${projectPath}`);
}
// Step 2: Configure AI tools
const toolSpinner = this.startSpinner('Configuring AI tools...');
const rootStubStatus = await this.configureAITools(
projectPath,
openspecDir,
config.aiTools
);
toolSpinner.stopAndPersist({
symbol: PALETTE.white('▌'),
text: PALETTE.white('AI tools configured'),
});
// Success message
this.displaySuccessMessage(
selectedTools,
created,
refreshed,
skippedExisting,
skipped,
extendMode,
rootStubStatus
);
}
private async getConfiguration(): Promise<OpenSpecConfig> {
const config: OpenSpecConfig = {
aiTools: []
};
private async validate(
projectPath: string,
_openspecPath: string
): Promise<boolean> {
const extendMode = await FileSystemUtils.directoryExists(_openspecPath);
// Single-select for better UX
const selectedTool = await select({
message: 'Which AI tool do you use?',
choices: AI_TOOLS.map(tool => ({
name: tool.available ? tool.name : `${tool.name} (coming soon)`,
// Check write permissions
if (!(await FileSystemUtils.ensureWritePermissions(projectPath))) {
throw new Error(`Insufficient permissions to write to ${projectPath}`);
}
return extendMode;
}
private async getConfiguration(
existingTools: Record<string, boolean>,
extendMode: boolean
): Promise<OpenSpecConfig> {
const selectedTools = await this.promptForAITools(
existingTools,
extendMode
);
return { aiTools: selectedTools };
}
private async promptForAITools(
existingTools: Record<string, boolean>,
extendMode: boolean
): Promise<string[]> {
const availableTools = AI_TOOLS.filter((tool) => tool.available);
const baseMessage = extendMode
? 'Which natively supported AI tools would you like to add or refresh?'
: 'Which natively supported AI tools do you use?';
const initialNativeSelection = extendMode
? availableTools
.filter((tool) => existingTools[tool.value])
.map((tool) => tool.value)
: [];
const initialSelected = Array.from(new Set(initialNativeSelection));
const choices: ToolWizardChoice[] = [
{
kind: 'heading',
value: '__heading-native__',
label: {
primary:
'Natively supported providers (✔ OpenSpec custom slash commands available)',
},
selectable: false,
},
...availableTools.map<ToolWizardChoice>((tool) => ({
kind: 'option',
value: tool.value,
disabled: !tool.available
}))
});
config.aiTools = [selectedTool as string];
label: parseToolLabel(tool.name),
configured: Boolean(existingTools[tool.value]),
selectable: true,
})),
...(availableTools.length
? ([
{
kind: 'info' as const,
value: LIST_SPACER_VALUE,
label: { primary: '' },
selectable: false,
},
] as ToolWizardChoice[])
: []),
{
kind: 'heading',
value: OTHER_TOOLS_HEADING_VALUE,
label: {
primary:
'Other tools (use Universal AGENTS.md for Codex, Amp, VS Code, GitHub Copilot, …)',
},
selectable: false,
},
{
kind: 'option',
value: ROOT_STUB_CHOICE_VALUE,
label: {
primary: 'Universal AGENTS.md',
annotation: 'always available',
},
configured: extendMode,
selectable: true,
},
];
return config;
return this.prompt({
extendMode,
baseMessage,
choices,
initialSelected,
});
}
private async getExistingToolStates(
projectPath: string
): Promise<Record<string, boolean>> {
const states: Record<string, boolean> = {};
for (const tool of AI_TOOLS) {
states[tool.value] = await this.isToolConfigured(projectPath, tool.value);
}
return states;
}
private async isToolConfigured(
projectPath: string,
toolId: string
): Promise<boolean> {
const configFile = ToolRegistry.get(toolId)?.configFileName;
if (
configFile &&
(await FileSystemUtils.fileExists(path.join(projectPath, configFile)))
)
return true;
const slashConfigurator = SlashCommandRegistry.get(toolId);
if (!slashConfigurator) return false;
for (const target of slashConfigurator.getTargets()) {
if (await FileSystemUtils.fileExists(path.join(projectPath, target.path)))
return true;
}
return false;
}
private async createDirectoryStructure(openspecPath: string): Promise<void> {
@@ -74,7 +575,7 @@ export class InitCommand {
openspecPath,
path.join(openspecPath, 'specs'),
path.join(openspecPath, 'changes'),
path.join(openspecPath, 'changes', 'archive')
path.join(openspecPath, 'changes', 'archive'),
];
for (const dir of directories) {
@@ -82,52 +583,222 @@ export class InitCommand {
}
}
private async generateFiles(openspecPath: string, config: OpenSpecConfig): Promise<void> {
private async generateFiles(
openspecPath: string,
config: OpenSpecConfig
): Promise<void> {
const context: ProjectContext = {
// Could be enhanced with prompts for project details
};
const templates = TemplateManager.getTemplates(context);
for (const template of templates) {
const filePath = path.join(openspecPath, template.path);
const content = typeof template.content === 'function'
? template.content(context)
: template.content;
const content =
typeof template.content === 'function'
? template.content(context)
: template.content;
await FileSystemUtils.writeFile(filePath, content);
}
}
private async configureAITools(projectPath: string, openspecDir: string, toolIds: string[]): Promise<void> {
private async configureAITools(
projectPath: string,
openspecDir: string,
toolIds: string[]
): Promise<RootStubStatus> {
const rootStubStatus = await this.configureRootAgentsStub(
projectPath,
openspecDir
);
for (const toolId of toolIds) {
const configurator = ToolRegistry.get(toolId);
if (configurator && configurator.isAvailable) {
await configurator.configure(projectPath, openspecDir);
}
const slashConfigurator = SlashCommandRegistry.get(toolId);
if (slashConfigurator && slashConfigurator.isAvailable) {
await slashConfigurator.generateAll(projectPath, openspecDir);
}
}
return rootStubStatus;
}
private displaySuccessMessage(openspecDir: string, config: OpenSpecConfig): void {
console.log(); // Empty line for spacing
ora().succeed('OpenSpec initialized successfully!');
// Get the selected tool name for display
const selectedToolId = config.aiTools[0];
const selectedTool = AI_TOOLS.find(t => t.value === selectedToolId);
const toolName = selectedTool ? selectedTool.name : 'your AI assistant';
console.log(`\nNext steps - Copy these prompts to ${toolName}:\n`);
console.log('────────────────────────────────────────────────────────────');
console.log('1. Populate your project context:');
console.log(' "Please read openspec/project.md and help me fill it out');
console.log(' with details about my project, tech stack, and conventions"\n');
console.log('2. Create your first change proposal:');
console.log(' "I want to add [YOUR FEATURE HERE]. Please create an');
console.log(' OpenSpec change proposal for this feature"\n');
console.log('3. Learn the OpenSpec workflow:');
console.log(' "Please explain the OpenSpec workflow from openspec/README.md');
console.log(' and how I should work with you on this project"');
console.log('────────────────────────────────────────────────────────────\n');
private async configureRootAgentsStub(
projectPath: string,
openspecDir: string
): Promise<RootStubStatus> {
const configurator = ToolRegistry.get('agents');
if (!configurator || !configurator.isAvailable) {
return 'skipped';
}
const stubPath = path.join(projectPath, configurator.configFileName);
const existed = await FileSystemUtils.fileExists(stubPath);
await configurator.configure(projectPath, openspecDir);
return existed ? 'updated' : 'created';
}
}
private displaySuccessMessage(
selectedTools: AIToolOption[],
created: AIToolOption[],
refreshed: AIToolOption[],
skippedExisting: AIToolOption[],
skipped: AIToolOption[],
extendMode: boolean,
rootStubStatus: RootStubStatus
): void {
console.log(); // Empty line for spacing
const successHeadline = extendMode
? 'OpenSpec tool configuration updated!'
: 'OpenSpec initialized successfully!';
ora().succeed(PALETTE.white(successHeadline));
console.log();
console.log(PALETTE.lightGray('Tool summary:'));
const summaryLines = [
rootStubStatus === 'created'
? `${PALETTE.white('▌')} ${PALETTE.white(
'Root AGENTS.md stub created for other assistants'
)}`
: null,
rootStubStatus === 'updated'
? `${PALETTE.lightGray('▌')} ${PALETTE.lightGray(
'Root AGENTS.md stub refreshed for other assistants'
)}`
: null,
created.length
? `${PALETTE.white('▌')} ${PALETTE.white(
'Created:'
)} ${this.formatToolNames(created)}`
: null,
refreshed.length
? `${PALETTE.lightGray('▌')} ${PALETTE.lightGray(
'Refreshed:'
)} ${this.formatToolNames(refreshed)}`
: null,
skippedExisting.length
? `${PALETTE.midGray('▌')} ${PALETTE.midGray(
'Skipped (already configured):'
)} ${this.formatToolNames(skippedExisting)}`
: null,
skipped.length
? `${PALETTE.darkGray('▌')} ${PALETTE.darkGray(
'Skipped:'
)} ${this.formatToolNames(skipped)}`
: null,
].filter((line): line is string => Boolean(line));
for (const line of summaryLines) {
console.log(line);
}
console.log();
console.log(
PALETTE.midGray(
'Use `openspec update` to refresh shared OpenSpec instructions in the future.'
)
);
// Get the selected tool name(s) for display
const toolName = this.formatToolNames(selectedTools);
console.log();
console.log(`Next steps - Copy these prompts to ${toolName}:`);
console.log(
chalk.gray('────────────────────────────────────────────────────────────')
);
console.log(PALETTE.white('1. Populate your project context:'));
console.log(
PALETTE.lightGray(
' "Please read openspec/project.md and help me fill it out'
)
);
console.log(
PALETTE.lightGray(
' with details about my project, tech stack, and conventions"\n'
)
);
console.log(PALETTE.white('2. Create your first change proposal:'));
console.log(
PALETTE.lightGray(
' "I want to add [YOUR FEATURE HERE]. Please create an'
)
);
console.log(
PALETTE.lightGray(' OpenSpec change proposal for this feature"\n')
);
console.log(PALETTE.white('3. Learn the OpenSpec workflow:'));
console.log(
PALETTE.lightGray(
' "Please explain the OpenSpec workflow from openspec/AGENTS.md'
)
);
console.log(
PALETTE.lightGray(' and how I should work with you on this project"')
);
console.log(
PALETTE.darkGray(
'────────────────────────────────────────────────────────────\n'
)
);
}
private formatToolNames(tools: AIToolOption[]): string {
const names = tools
.map((tool) => tool.successLabel ?? tool.name)
.filter((name): name is string => Boolean(name));
if (names.length === 0)
return PALETTE.lightGray('your AGENTS.md-compatible assistant');
if (names.length === 1) return PALETTE.white(names[0]);
const base = names.slice(0, -1).map((name) => PALETTE.white(name));
const last = PALETTE.white(names[names.length - 1]);
return `${base.join(PALETTE.midGray(', '))}${
base.length ? PALETTE.midGray(', and ') : ''
}${last}`;
}
private renderBanner(_extendMode: boolean): void {
const rows = ['', '', '', '', ''];
for (const char of 'OPENSPEC') {
const glyph = LETTER_MAP[char] ?? LETTER_MAP[' '];
for (let i = 0; i < rows.length; i += 1) {
rows[i] += `${glyph[i]} `;
}
}
const rowStyles = [
PALETTE.white,
PALETTE.lightGray,
PALETTE.midGray,
PALETTE.lightGray,
PALETTE.white,
];
console.log();
rows.forEach((row, index) => {
console.log(rowStyles[index](row.replace(/\s+$/u, '')));
});
console.log();
console.log(PALETTE.white('Welcome to OpenSpec!'));
console.log();
}
private startSpinner(text: string) {
return ora({
text,
stream: process.stdout,
color: 'gray',
spinner: PROGRESS_SPINNER,
}).start();
}
}
+3 -2
View File
@@ -150,7 +150,7 @@ export class ChangeParser extends MarkdownParser {
private parseRenames(content: string): Array<{ from: string; to: string }> {
const renames: Array<{ from: string; to: string }> = [];
const lines = content.split('\n');
const lines = ChangeParser.normalizeContent(content).split('\n');
let currentRename: { from?: string; to?: string } = {};
@@ -177,7 +177,8 @@ export class ChangeParser extends MarkdownParser {
}
private parseSectionsFromContent(content: string): Section[] {
const lines = content.split('\n');
const normalizedContent = ChangeParser.normalizeContent(content);
const lines = normalizedContent.split('\n');
const sections: Section[] = [];
const stack: Section[] = [];
+6 -1
View File
@@ -12,10 +12,15 @@ export class MarkdownParser {
private currentLine: number;
constructor(content: string) {
this.lines = content.split('\n');
const normalized = MarkdownParser.normalizeContent(content);
this.lines = normalized.split('\n');
this.currentLine = 0;
}
protected static normalizeContent(content: string): string {
return content.replace(/\r\n?/g, '\n');
}
parseSpec(name: string): Spec {
const sections = this.parseSections();
const purpose = this.findSection(sections, 'Purpose')?.content || '';
+11 -5
View File
@@ -22,7 +22,8 @@ const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/;
* Extracts the Requirements section from a spec file and parses requirement blocks.
*/
export function extractRequirementsSection(content: string): RequirementsSectionParts {
const lines = content.split('\n');
const normalized = normalizeLineEndings(content);
const lines = normalized.split('\n');
const reqHeaderIndex = lines.findIndex(l => /^##\s+Requirements\s*$/i.test(l));
if (reqHeaderIndex === -1) {
@@ -102,11 +103,16 @@ export interface DeltaPlan {
renamed: Array<{ from: string; to: string }>;
}
function normalizeLineEndings(content: string): string {
return content.replace(/\r\n?/g, '\n');
}
/**
* Parse a delta-formatted spec change file content into a DeltaPlan with raw blocks.
*/
export function parseDeltaSpec(content: string): DeltaPlan {
const sections = splitTopLevelSections(content);
const normalized = normalizeLineEndings(content);
const sections = splitTopLevelSections(normalized);
const added = parseRequirementBlocksFromSection(sections['ADDED Requirements'] || '');
const modified = parseRequirementBlocksFromSection(sections['MODIFIED Requirements'] || '');
const removedNames = parseRemovedNames(sections['REMOVED Requirements'] || '');
@@ -136,7 +142,7 @@ function splitTopLevelSections(content: string): Record<string, string> {
function parseRequirementBlocksFromSection(sectionBody: string): RequirementBlock[] {
if (!sectionBody) return [];
const lines = sectionBody.split('\n');
const lines = normalizeLineEndings(sectionBody).split('\n');
const blocks: RequirementBlock[] = [];
let i = 0;
while (i < lines.length) {
@@ -161,7 +167,7 @@ function parseRequirementBlocksFromSection(sectionBody: string): RequirementBloc
function parseRemovedNames(sectionBody: string): string[] {
if (!sectionBody) return [];
const names: string[] = [];
const lines = sectionBody.split('\n');
const lines = normalizeLineEndings(sectionBody).split('\n');
for (const line of lines) {
const m = line.match(REQUIREMENT_HEADER_REGEX);
if (m) {
@@ -180,7 +186,7 @@ function parseRemovedNames(sectionBody: string): string[] {
function parseRenamedPairs(sectionBody: string): Array<{ from: string; to: string }> {
if (!sectionBody) return [];
const pairs: Array<{ from: string; to: string }> = [];
const lines = sectionBody.split('\n');
const lines = normalizeLineEndings(sectionBody).split('\n');
let current: { from?: string; to?: string } = {};
for (const line of lines) {
const fromMatch = line.match(/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
+8
View File
@@ -0,0 +1,8 @@
import chalk from 'chalk';
export const PALETTE = {
white: chalk.hex('#f4f4f4'),
lightGray: chalk.hex('#c8c8c8'),
midGray: chalk.hex('#8a8a8a'),
darkGray: chalk.hex('#4a4a4a')
};
+16
View File
@@ -0,0 +1,16 @@
export const agentsRootStubTemplate = `# 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.
`;
@@ -1,4 +1,4 @@
export const readmeTemplate = `# OpenSpec Instructions
export const agentsTemplate = `# OpenSpec Instructions
Instructions for AI coding assistants using OpenSpec for spec-driven development.
@@ -40,20 +40,28 @@ Skip proposal for:
- 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\` 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. **Mark complete immediately** - Update \`- [x]\` after each task
6. **Validate strictly** - Run \`openspec validate [change] --strict\` and address issues
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] --skip-specs\` for tooling-only changes
- Use \`openspec archive [change] --skip-specs --yes\` for tooling-only changes
- Run \`openspec validate --strict\` to confirm the archived change passes checks
## Before Any Task
@@ -89,7 +97,7 @@ openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec diff [change] # Show spec differences
openspec validate [item] # Validate changes or specs
openspec archive [change] # Archive after deployment
openspec archive [change] [--yes|-y] # Archive after deployment (add --yes for non-interactive runs)
# Project management
openspec init [path] # Initialize OpenSpec
@@ -111,6 +119,7 @@ openspec validate [change] --strict
- \`--strict\` - Comprehensive validation
- \`--no-interactive\` - Disable prompts
- \`--skip-specs\` - Archive without spec updates
- \`--yes\`/\`-y\` - Skip confirmation prompts (non-interactive archive)
## Directory Structure
@@ -256,6 +265,19 @@ Every requirement MUST have at least one scenario.
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
@@ -428,7 +450,7 @@ openspec list # What's in progress?
openspec show [item] # View details
openspec diff [change] # What's changing?
openspec validate --strict # Is it correct?
openspec archive [change] # Mark complete
openspec archive [change] [--yes|-y] # Mark complete (add --yes for automation)
\`\`\`
Remember: Specs are truth. Changes are proposals. Keep them in sync.
+1 -105
View File
@@ -1,105 +1 @@
export const claudeTemplate = `# OpenSpec Project
This project uses OpenSpec for spec-driven development. Specifications are the source of truth.
See @openspec/README.md for detailed conventions and guidelines.
## Three-Stage Workflow
### Stage 1: Creating Changes
Create proposal for: features, breaking changes, architecture changes
Skip proposal for: bug fixes, typos, non-breaking updates
### Stage 2: Implementing Changes
1. Read proposal.md to understand the change
2. Read design.md if it exists for technical context
3. Read tasks.md for implementation checklist
4. Complete tasks one by one
5. Mark each task complete immediately: \`- [x]\`
6. Validate strictly: \`openspec validate [change] --strict\`
7. Approval gate: Do not start implementation until the proposal is approved
### Stage 3: Archiving
After deployment, use \`openspec archive [change]\` (add \`--skip-specs\` for tooling-only changes)
## Before Any Task
**Always:**
- Check existing specs: \`openspec list --specs\`
- Check active changes: \`openspec list\`
- Read relevant specs before creating new ones
- Prefer modifying existing specs over creating duplicates
## CLI Quick Reference
\`\`\`bash
# Essential
openspec list # Active changes
openspec list --specs # Existing specifications
openspec show [item] # View details
openspec validate --strict # Validate thoroughly
openspec archive [change] # Archive after deployment
# Interactive
openspec show # Prompts for selection
openspec validate # Bulk validation
# Debugging
openspec show [change] --json --deltas-only
\`\`\`
## Creating Changes
1. **Directory:** \`changes/[change-id]/\`
- Change ID naming: kebab-case, verb-led (\`add-\`, \`update-\`, \`remove-\`, \`refactor-\`), unique (append \`-2\`, \`-3\` if needed)
2. **Files:**
- \`proposal.md\` - Why, what, impact
- \`tasks.md\` - Implementation checklist
- \`design.md\` - Only if needed (cross-cutting, new deps/data model, security/perf/migration complexity, or high ambiguity)
- \`specs/[capability]/spec.md\` - Delta changes (ADDED/MODIFIED/REMOVED). For multiple capabilities, include multiple files.
3. **If ambiguous:** ask 1–2 clarifying questions before scaffolding
## Search Guidance
- Enumerate specs: \`openspec spec list --long\` (or \`--json\`)
- Enumerate changes: \`openspec list\`
- Show details: \`openspec show <spec-id> --type spec\`, \`openspec show <change-id> --json --deltas-only\`
- Full-text search (use ripgrep): \`rg -n "Requirement:|Scenario:" openspec/specs\`
## Critical: Scenario Format
**CORRECT:**
\`\`\`markdown
#### Scenario: User login
- **WHEN** valid credentials
- **THEN** return token
\`\`\`
**WRONG:** Using bullets (- **Scenario**), bold (**Scenario:**), or ### headers
Every requirement MUST have scenarios using \`#### Scenario:\` format.
## Complexity Management
**Default to minimal:**
- <100 lines of new code
- Single-file implementations
- No frameworks without justification
- Boring, proven patterns
**Only add complexity with:**
- Performance data showing need
- Concrete scale requirements (>1000 users)
- Multiple proven use cases
## Troubleshooting
**"Change must have at least one delta"**
- Check \`changes/[name]/specs/\` exists
- Verify operation prefixes (## ADDED Requirements)
**"Requirement must have at least one scenario"**
- Use \`#### Scenario:\` format (4 hashtags)
- Don't use bullets or bold
**Debug:** \`openspec show [change] --json --deltas-only\`
`;
export { agentsRootStubTemplate as claudeTemplate } from './agents-root-stub.js';

Some files were not shown because too many files have changed in this diff Show More