Compare commits

..
Author SHA1 Message Date
Tabish Bidiwale 82ba1f504e merge: resolve conflicts with main branch 2025-09-09 14:12:05 +10:00
Tabish Bidiwale d54fcc97f2 docs: mark completed tasks for diff command removal 2025-09-09 14:07:52 +10:00
Tabish Bidiwale ebff738860 feat: remove diff command in favor of show command
The diff command added unnecessary complexity and duplicated functionality
already available through the show command. Users can now use:
- `openspec show <change>` for structured change viewing
- `openspec show <change> --json --deltas-only` for delta-only views
- Standard git diff or other tools for file comparisons

This change:
- Removes ~227 lines of code and the jest-diff dependency
- Simplifies the CLI interface
- Reduces maintenance burden
- Aligns with verb-first command structure
2025-09-09 14:05:41 +10:00
Tabish Bidiwale 1bdaeef4da Update README.md 2025-09-07 09:16:53 +10:00
11 changed files with 580 additions and 978 deletions
+1 -5
View File
@@ -10,7 +10,7 @@
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 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.
## Why OpenSpec?
@@ -118,9 +118,6 @@ AI: "Following the tasks in openspec/changes/add-user-profile-api/tasks.md:
# View active changes (what's being worked on)
openspec list
# See the difference between proposed and current specs
openspec diff add-2fa
# Validate your changes are properly formatted
openspec validate add-2fa --strict
@@ -137,7 +134,6 @@ openspec list # See what changes you're working on
openspec archive <change> # Mark a change as complete after deployment
# Also useful:
openspec diff <change> # See what specs will change
openspec validate <change> # Check formatting before committing
openspec show <change> # View change details
```
+3 -141
View File
@@ -2,16 +2,6 @@
Instructions for AI coding assistants using OpenSpec for spec-driven development.
## TL;DR Quick Checklist
- Search existing work: `openspec spec list --long`, `openspec list` (use `rg` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique `change-id`: kebab-case, verb-led (`add-`, `update-`, `remove-`, `refactor-`)
- Scaffold: `proposal.md`, `tasks.md`, `design.md` (only if needed), and delta specs per affected capability
- Write deltas: use `## ADDED|MODIFIED|REMOVED|RENAMED Requirements`; include at least one `#### Scenario:` per requirement
- Validate: `openspec validate [change-id] --strict` and fix issues
- Request approval: Do not start implementation until proposal is approved
## Three-Stage Workflow
### Stage 1: Creating Changes
@@ -22,17 +12,6 @@ Create proposal when you need to:
- Optimize performance (changes behavior)
- Update security patterns
Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"
Loose matching guidance:
- Contains one of: `proposal`, `change`, `spec`
- With one of: `create`, `plan`, `make`, `start`, `help`
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
@@ -46,8 +25,6 @@ Skip proposal for:
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
7. **Approval gate** - Do not start implementation until the proposal is reviewed and approved
### Stage 3: Archiving Changes
After deployment, create separate PR to:
@@ -68,15 +45,6 @@ After deployment, create separate PR to:
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use `openspec show [spec]` to review current state
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
### Search Guidance
- Enumerate specs: `openspec spec list --long` (or `--json` for scripts)
- Enumerate changes: `openspec list` (or `openspec change list --json` - deprecated but available)
- Show details:
- Spec: `openspec show <spec-id> --type spec` (use `--json` for filters)
- Change: `openspec show <change-id> --json --deltas-only`
- Full-text search (use ripgrep): `rg -n "Requirement:|Scenario:" openspec/specs`
## Quick Start
@@ -87,7 +55,6 @@ 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
@@ -125,7 +92,7 @@ openspec/
│ ├── [change-name]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional; see criteria)
│ │ ├── design.md # Technical decisions (optional)
│ │ └── specs/ # Delta changes
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
@@ -148,7 +115,7 @@ New request?
### Proposal Structure
1. **Create directory:** `changes/[change-id]/` (kebab-case, verb-led, unique)
1. **Create directory:** `changes/[descriptive-name]/`
2. **Write proposal.md:**
```markdown
@@ -183,7 +150,6 @@ The system SHALL provide...
**Reason**: [Why removing]
**Migration**: [How to handle]
```
If multiple capabilities are affected, create multiple delta files under `changes/[change-id]/specs/<capability>/spec.md`—one per capability.
4. **Create tasks.md:**
```markdown
@@ -194,36 +160,6 @@ If multiple capabilities are affected, create multiple delta files under `change
- [ ] 1.4 Write tests
```
5. **Create design.md when needed:**
Create `design.md` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding
Minimal `design.md` skeleton:
```markdown
## Context
[Background, constraints, stakeholders]
## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]
## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]
## Risks / Trade-offs
- [Risk] → Mitigation
## Migration Plan
[Steps, rollback]
## Open Questions
- [...]
```
## Spec File Format
### Critical: Scenario Formatting
@@ -244,9 +180,6 @@ Minimal `design.md` skeleton:
Every requirement MUST have at least one scenario.
### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
### Delta Operations
- `## ADDED Requirements` - New capabilities
@@ -256,13 +189,6 @@ Every requirement MUST have at least one scenario.
Headers matched with `trim(header)` - whitespace ignored.
Example for RENAMED:
```markdown
## RENAMED Requirements
- FROM: `### Requirement: Login`
- TO: `### Requirement: User Authentication`
```
## Troubleshooting
### Common Errors
@@ -292,64 +218,6 @@ openspec show [change] --json | jq '.deltas'
openspec show [spec] --json -r 1
```
## Happy Path Script
```bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes
# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\n...\n\n## What Changes\n- ...\n\n## Impact\n- ...\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\n- [ ] 1.1 ...\n" > openspec/changes/$CHANGE/tasks.md
# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.
#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF
# 4) Validate
openspec validate $CHANGE --strict
```
## Multi-Capability Example
```
openspec/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # ADDED: Two-Factor Authentication
└── notifications/
└── spec.md # ADDED: OTP email notification
```
auth/spec.md
```markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...
```
notifications/spec.md
```markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...
```
## Best Practices
### Simplicity First
@@ -375,11 +243,6 @@ Only add complexity with:
- 10-minute understandability rule
- Split if description needs "AND"
### Change ID Naming
- Use kebab-case, short and descriptive: `add-two-factor-auth`
- Prefer verb-led prefixes: `add-`, `update-`, `remove-`, `refactor-`
- Ensure uniqueness; if taken, append `-2`, `-3`, etc.
## Tool Selection Guide
| Task | Tool | Why |
@@ -426,9 +289,8 @@ 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
```
Remember: Specs are truth. Changes are proposals. Keep them in sync.
Remember: Specs are truth. Changes are proposals. Keep them in sync.
@@ -0,0 +1,81 @@
# Remove Diff Command
## Problem
The `openspec diff` command adds unnecessary complexity to the OpenSpec CLI for several reasons:
1. **Redundant functionality**: The `openspec show` command already provides comprehensive visualization of changes through structured JSON output and markdown rendering
2. **Maintenance burden**: The diff command requires a separate dependency (jest-diff) and additional code complexity (~227 lines)
3. **Limited value**: Developers can achieve better diff visualization using existing tools:
- Git diff for actual file changes
- The `show` command for structured change viewing
- Standard diff utilities for comparing spec files directly
4. **Inconsistent with verb-noun pattern**: The command doesn't follow the preferred verb-first command structure that other commands are migrating to
## Solution
Remove the `openspec diff` command entirely and guide users to more appropriate alternatives:
1. **For viewing change content**: Use `openspec show <change-name>` which provides:
- Structured JSON output with `--json` flag
- Markdown rendering for human-readable format
- Delta-only views with `--deltas-only` flag
- Full spec content visualization
2. **For comparing files**: Use standard tools:
- `git diff` for version control comparisons
- System diff utilities for file-by-file comparisons
- IDE diff viewers for visual comparisons
## Benefits
- **Reduced complexity**: Removes ~227 lines of code and the jest-diff dependency
- **Clearer user journey**: Directs users to the canonical `show` command for viewing changes
- **Lower maintenance**: Fewer commands to maintain and test
- **Better alignment**: Focuses on the core OpenSpec workflow without redundant features
## Implementation
### Files to Remove
- `/src/core/diff.ts` - The entire diff command implementation
- `/openspec/specs/cli-diff/spec.md` - The diff command specification
### Files to Update
- `/src/cli/index.ts` - Remove diff command registration (lines 8, 84-96)
- `/package.json` - Remove jest-diff dependency
- `/README.md` - Remove diff command documentation
- `/openspec/README.md` - Remove diff command references
- Various documentation files mentioning `openspec diff`
### Migration Guide for Users
Users currently using `openspec diff` should transition to:
```bash
# Before
openspec diff add-feature
# After - view the change proposal
openspec show add-feature
# After - view only the deltas
openspec show add-feature --json --deltas-only
# After - use git for file comparisons
git diff openspec/specs openspec/changes/add-feature/specs
```
## Risks
- **User disruption**: Existing users may have workflows depending on the diff command
- Mitigation: Provide clear migration guide and deprecation period
- **Loss of visual diff**: The colored, unified diff format will no longer be available
- Mitigation: Users can use git diff or other tools for visual comparisons
## Success Metrics
- Successful removal with no broken dependencies
- Documentation updated to reflect the change
- Tests passing without the diff command
- Reduced package size from removing jest-diff dependency
@@ -0,0 +1,41 @@
# Remove Diff Command - Tasks
## 1. Remove Core Implementation
- [x] Delete `/src/core/diff.ts`
- [x] Remove DiffCommand import from `/src/cli/index.ts`
- [x] Remove diff command registration from CLI
## 2. Remove Specifications
- [x] Delete `/openspec/specs/cli-diff/spec.md`
- [x] Archive the spec for historical reference if needed
## 3. Update Dependencies
- [x] Remove jest-diff from package.json dependencies
- [x] Run pnpm install to update lock file
## 4. Update Documentation
- [x] Update main README.md to remove diff command references
- [x] Update openspec/README.md to remove diff command from command list
- [x] Update CLAUDE.md template if it mentions diff command
- [x] Update any example workflows that use diff command
## 5. Update Related Files
- [x] Search and update any remaining references to "openspec diff" in:
- Template files
- Test files (if any exist for diff command)
- 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
- [x] Test that show command provides adequate replacement functionality
## 8. Documentation of Alternative Workflows
- [x] Document how to use `openspec show` for viewing changes
- [x] Document how to use git diff for file comparisons
- [x] Add migration guide to help text or documentation
-123
View File
@@ -1,123 +0,0 @@
# CLI Diff Command Specification
## Purpose
The `openspec diff` command provides developers with a visual comparison between proposed spec changes and the current deployed specs.
## Command Syntax
```bash
openspec diff [change-name]
```
## Requirements
### Requirement: Without Arguments
The command SHALL provide an interactive selection when no change is specified.
#### Scenario: Running without arguments
- **WHEN** running `openspec diff` without arguments
- **THEN** list all available changes in the `changes/` directory (excluding archive)
- **AND** prompt user to select a change
### Requirement: With Change Name
The command SHALL compare specs when a specific change is provided.
#### Scenario: Running with change name
- **WHEN** running `openspec diff <change-name>`
- **THEN** compare all spec files in `changes/<change-name>/specs/` with corresponding files in `specs/`
### Requirement: Diff Output
The command SHALL show a requirement-level comparison displaying only changed requirements.
#### Scenario: Side-by-side comparison of changes
- **WHEN** running `openspec diff <change>`
- **THEN** display only requirements that have changed
- **AND** show them in a side-by-side format that:
- Clearly shows the current version on the left
- Shows the future version on the right
- Indicates new requirements (not in current)
- Indicates removed requirements (not in future)
- Aligns modified requirements for easy comparison
### Requirement: Color Support
The command SHALL enhance readability with colors when supported.
#### Scenario: Terminal with color support
- **WHEN** terminal supports colors
- **THEN** display:
- Removed lines in red
- Added lines in green
- File headers in bold
- Context lines in default color
### Requirement: Error Handling
The command SHALL provide clear error messages for various failure conditions.
#### Scenario: Change not found
- **WHEN** specified change doesn't exist
- **THEN** display error "Change '<name>' not found"
#### Scenario: No specs in change
- **WHEN** no specs directory in change
- **THEN** display "No spec changes found for '<name>'"
#### Scenario: Missing changes directory
- **WHEN** changes directory doesn't exist
- **THEN** display "No OpenSpec changes directory found"
### Requirement: Validation
The command SHALL validate that changes can be applied successfully.
#### Scenario: Invalid delta references
- **WHEN** delta references non-existent requirement
- **THEN** show error message with specific requirement
- **AND** continue showing other valid changes
- **AND** clearly mark failed changes in the output
### Requirement: Diff Command Enhancement
The diff command SHALL validate change structure before displaying differences.
#### Scenario: Validate before diff
- **WHEN** executing `openspec diff change-name`
- **THEN** validate change structure
- **AND** show validation warnings if present
- **AND** continue with diff display
## Examples
```bash
# View diff for specific change
$ openspec diff add-auth-feature
--- specs/user-auth/spec.md
+++ changes/add-auth-feature/specs/user-auth/spec.md
@@ -10,6 +10,8 @@
Users SHALL authenticate with email and password.
+Users MAY authenticate with OAuth providers.
+
WHEN credentials are valid THEN issue JWT token.
# List all changes and select
$ openspec diff
Available changes:
1. add-auth-feature
2. update-payment-flow
3. add-status-command
Select a change (1-3):
```
-1
View File
@@ -63,7 +63,6 @@
"@inquirer/prompts": "^7.8.0",
"chalk": "^5.5.0",
"commander": "^14.0.0",
"jest-diff": "^30.0.5",
"ora": "^8.2.0",
"zod": "^4.0.17"
}
-83
View File
@@ -17,9 +17,6 @@ importers:
commander:
specifier: ^14.0.0
version: 14.0.0
jest-diff:
specifier: ^30.0.5
version: 30.0.5
ora:
specifier: ^8.2.0
version: 8.2.0
@@ -390,18 +387,6 @@ packages:
'@types/node':
optional: true
'@jest/diff-sequences@30.0.1':
resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/get-type@30.0.1':
resolution: {integrity: sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jest/schemas@30.0.5':
resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
'@jridgewell/sourcemap-codec@1.5.4':
resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
@@ -526,9 +511,6 @@ packages:
cpu: [x64]
os: [win32]
'@sinclair/typebox@0.34.38':
resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==}
'@types/chai@5.2.2':
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
@@ -598,10 +580,6 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@5.2.0:
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
engines: {node: '>=10'}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@@ -629,10 +607,6 @@ packages:
resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==}
engines: {node: '>=18'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chalk@5.5.0:
resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
@@ -793,10 +767,6 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
human-id@4.1.1:
resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==}
hasBin: true
@@ -852,10 +822,6 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
jest-diff@30.0.5:
resolution: {integrity: sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
@@ -996,19 +962,12 @@ packages:
engines: {node: '>=10.13.0'}
hasBin: true
pretty-format@30.0.5:
resolution: {integrity: sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==}
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
quansync@0.2.11:
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
read-yaml-file@1.1.0:
resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
engines: {node: '>=6'}
@@ -1107,10 +1066,6 @@ packages:
strip-literal@3.0.0:
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
@@ -1608,14 +1563,6 @@ snapshots:
optionalDependencies:
'@types/node': 24.2.0
'@jest/diff-sequences@30.0.1': {}
'@jest/get-type@30.0.1': {}
'@jest/schemas@30.0.5':
dependencies:
'@sinclair/typebox': 0.34.38
'@jridgewell/sourcemap-codec@1.5.4': {}
'@manypkg/find-root@1.1.0':
@@ -1708,8 +1655,6 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.46.2':
optional: true
'@sinclair/typebox@0.34.38': {}
'@types/chai@5.2.2':
dependencies:
'@types/deep-eql': 4.0.2
@@ -1791,8 +1736,6 @@ snapshots:
dependencies:
color-convert: 2.0.1
ansi-styles@5.2.0: {}
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
@@ -1819,11 +1762,6 @@ snapshots:
loupe: 3.2.0
pathval: 2.0.1
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
chalk@5.5.0: {}
chardet@0.7.0: {}
@@ -1985,8 +1923,6 @@ snapshots:
graceful-fs@4.2.11: {}
has-flag@4.0.0: {}
human-id@4.1.1: {}
iconv-lite@0.4.24:
@@ -2023,13 +1959,6 @@ snapshots:
isexe@2.0.0: {}
jest-diff@30.0.5:
dependencies:
'@jest/diff-sequences': 30.0.1
'@jest/get-type': 30.0.1
chalk: 4.1.2
pretty-format: 30.0.5
js-tokens@9.0.1: {}
js-yaml@3.14.1:
@@ -2143,18 +2072,10 @@ snapshots:
prettier@2.8.8: {}
pretty-format@30.0.5:
dependencies:
'@jest/schemas': 30.0.5
ansi-styles: 5.2.0
react-is: 18.3.1
quansync@0.2.11: {}
queue-microtask@1.2.3: {}
react-is@18.3.1: {}
read-yaml-file@1.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -2264,10 +2185,6 @@ snapshots:
dependencies:
js-tokens: 9.0.1
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
term-size@2.2.1: {}
tinybench@2.9.0: {}
-15
View File
@@ -5,7 +5,6 @@ import path from 'path';
import { promises as fs } from 'fs';
import { InitCommand } from '../core/init.js';
import { UpdateCommand } from '../core/update.js';
import { DiffCommand } from '../core/diff.js';
import { ListCommand } from '../core/list.js';
import { ArchiveCommand } from '../core/archive.js';
import { registerSpecCommand } from '../commands/spec.js';
@@ -81,20 +80,6 @@ program
}
});
program
.command('diff [change-name]')
.description('Show differences between proposed spec changes and current specs (includes validation warnings)')
.action(async (changeName?: string) => {
try {
const diffCommand = new DiffCommand();
await diffCommand.execute(changeName);
} catch (error) {
console.log(); // Empty line for spacing
ora().fail(`Error: ${(error as Error).message}`);
process.exit(1);
}
});
program
.command('list')
.description('List items (changes by default). Use --specs to list specs.')
-227
View File
@@ -1,227 +0,0 @@
import { promises as fs } from 'fs';
import path from 'path';
import chalk from 'chalk';
import { diffStringsUnified } from 'jest-diff';
import { select } from '@inquirer/prompts';
import { Validator } from './validation/validator.js';
// Constants
const ARCHIVE_DIR = 'archive';
const MARKDOWN_EXT = '.md';
const OPENSPEC_DIR = 'openspec';
const CHANGES_DIR = 'changes';
const SPECS_DIR = 'specs';
export class DiffCommand {
private filesChanged: number = 0;
private linesAdded: number = 0;
private linesRemoved: number = 0;
async execute(changeName?: string): Promise<void> {
const changesDir = path.join(process.cwd(), OPENSPEC_DIR, CHANGES_DIR);
try {
await fs.access(changesDir);
} catch {
throw new Error('No OpenSpec changes directory found');
}
if (!changeName) {
changeName = await this.selectChange(changesDir);
if (!changeName) return;
}
const changeDir = path.join(changesDir, changeName);
try {
await fs.access(changeDir);
} catch {
throw new Error(`Change '${changeName}' not found`);
}
const changeSpecsDir = path.join(changeDir, SPECS_DIR);
try {
await fs.access(changeSpecsDir);
} catch {
console.log(`No spec changes found for '${changeName}'`);
return;
}
// Validate specs and show warnings (non-blocking)
const validator = new Validator();
let hasWarnings = false;
try {
const entries = await fs.readdir(changeSpecsDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const specFile = path.join(changeSpecsDir, entry.name, 'spec.md');
try {
await fs.access(specFile);
const report = await validator.validateSpec(specFile);
if (report.issues.length > 0) {
const warnings = report.issues.filter(i => i.level === 'WARNING');
const errors = report.issues.filter(i => i.level === 'ERROR');
if (errors.length > 0 || warnings.length > 0) {
if (!hasWarnings) {
console.log(chalk.yellow('\n⚠️ Validation warnings found:'));
hasWarnings = true;
}
console.log(chalk.yellow(`\n ${entry.name}/spec.md:`));
for (const issue of errors) {
console.log(chalk.red(` ✗ ${issue.message}`));
}
for (const issue of warnings) {
console.log(chalk.yellow(` ⚠ ${issue.message}`));
}
}
}
} catch {
// Spec file doesn't exist, skip validation
}
}
}
if (hasWarnings) {
console.log(chalk.yellow('\nConsider fixing these issues before archiving.\n'));
}
} catch {
// No specs directory, skip validation
}
// Reset counters
this.filesChanged = 0;
this.linesAdded = 0;
this.linesRemoved = 0;
await this.showDiffs(changeSpecsDir);
// Show summary
if (this.filesChanged > 0) {
console.log(chalk.bold(`\n📊 Summary: ${this.filesChanged} file(s) changed, ${chalk.green(`+${this.linesAdded}`)} ${chalk.red(`-${this.linesRemoved}`)}`));
}
}
private async selectChange(changesDir: string): Promise<string | undefined> {
const entries = await fs.readdir(changesDir, { withFileTypes: true });
const changes = entries
.filter(entry => entry.isDirectory() && entry.name !== ARCHIVE_DIR)
.map(entry => entry.name);
if (changes.length === 0) {
console.log('No changes found');
return undefined;
}
console.log('Available changes:');
const choices = changes.map((name) => ({
name: name,
value: name
}));
const answer = await select({
message: 'Select a change',
choices
});
return answer as string;
}
private async showDiffs(changeSpecsDir: string): Promise<void> {
const currentSpecsDir = path.join(process.cwd(), OPENSPEC_DIR, SPECS_DIR);
await this.walkAndDiff(changeSpecsDir, currentSpecsDir, '');
}
private async walkAndDiff(changeDir: string, currentDir: string, relativePath: string): Promise<void> {
const entries = await fs.readdir(path.join(changeDir, relativePath), { withFileTypes: true });
for (const entry of entries) {
const entryPath = path.join(relativePath, entry.name);
if (entry.isDirectory()) {
await this.walkAndDiff(changeDir, currentDir, entryPath);
} else if (entry.isFile() && entry.name.endsWith(MARKDOWN_EXT)) {
await this.diffFile(
path.join(changeDir, entryPath),
path.join(currentDir, entryPath),
entryPath
);
}
}
}
private async diffFile(changePath: string, currentPath: string, displayPath: string): Promise<void> {
let changeContent = '';
let currentContent = '';
let isNewFile = false;
let isDeleted = false;
try {
changeContent = await fs.readFile(changePath, 'utf-8');
} catch {
changeContent = '';
}
try {
currentContent = await fs.readFile(currentPath, 'utf-8');
} catch {
currentContent = '';
isNewFile = true;
}
if (changeContent === currentContent) {
return;
}
if (changeContent === '' && currentContent !== '') {
isDeleted = true;
}
// Enhanced header with file status
console.log(chalk.bold.cyan(`\n${'═'.repeat(60)}`));
console.log(chalk.bold.cyan(`📄 ${displayPath}`));
if (isNewFile) {
console.log(chalk.green(` Status: NEW FILE`));
} else if (isDeleted) {
console.log(chalk.red(` Status: DELETED`));
} else {
console.log(chalk.yellow(` Status: MODIFIED`));
}
// Use jest-diff for the actual diff with custom options
const diffOptions = {
aAnnotation: 'Current',
bAnnotation: 'Proposed',
aColor: chalk.red,
bColor: chalk.green,
commonColor: chalk.gray,
contextLines: 3,
expand: false,
includeChangeCounts: true,
};
const diff = diffStringsUnified(currentContent, changeContent, diffOptions);
// Count lines for statistics (approximate)
const addedLines = (diff.match(/^\+[^+]/gm) || []).length;
const removedLines = (diff.match(/^-[^-]/gm) || []).length;
console.log(chalk.gray(` Lines: ${chalk.green(`+${addedLines}`)} ${chalk.red(`-${removedLines}`)}`));
console.log(chalk.bold.cyan(`${'─'.repeat(60)}\n`));
// Display the diff
console.log(diff);
// Update counters
this.filesChanged++;
this.linesAdded += addedLines;
this.linesRemoved += removedLines;
}
}
+3 -15
View File
@@ -16,8 +16,6 @@ Skip proposal for: bug fixes, typos, non-breaking updates
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)
@@ -37,7 +35,6 @@ After deployment, use \`openspec archive [change]\` (add \`--skip-specs\` for to
openspec list # Active changes
openspec list --specs # Existing specifications
openspec show [item] # View details
openspec diff [change] # Show spec differences
openspec validate --strict # Validate thoroughly
openspec archive [change] # Archive after deployment
@@ -51,20 +48,11 @@ 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)
1. **Directory:** \`changes/[descriptive-name]/\`
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\`
- \`specs/[capability]/spec.md\` - Delta changes (ADDED/MODIFIED/REMOVED)
## Critical: Scenario Format
@@ -103,4 +91,4 @@ Every requirement MUST have scenarios using \`#### Scenario:\` format.
- Don't use bullets or bold
**Debug:** \`openspec show [change] --json --deltas-only\`
`;
`;
+451 -368
View File
@@ -1,435 +1,518 @@
export const readmeTemplate = `# OpenSpec Instructions
Instructions for AI coding assistants using OpenSpec for spec-driven development.
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.
## TL;DR Quick Checklist
## Core Principle
- Search existing work: \`openspec spec list --long\`, \`openspec list\` (use \`rg\` only for full-text search)
- Decide scope: new capability vs modify existing capability
- Pick a unique \`change-id\`: kebab-case, verb-led (\`add-\`, \`update-\`, \`remove-\`, \`refactor-\`)
- Scaffold: \`proposal.md\`, \`tasks.md\`, \`design.md\` (only if needed), and delta specs per affected capability
- Write deltas: use \`## ADDED|MODIFIED|REMOVED|RENAMED Requirements\`; include at least one \`#### Scenario:\` per requirement
- Validate: \`openspec validate [change-id] --strict\` and fix issues
- Request approval: Do not start implementation until proposal is approved
OpenSpec is an AI-native system for change-driven development where:
- **Specs** (\`specs/\`) reflect what IS currently built and deployed
- **Changes** (\`changes/\`) contain proposals for what SHOULD be changed
- **AI drives the process** - You generate proposals, humans review and approve
- **Specs are living documentation** - Always kept in sync with deployed code
## Three-Stage Workflow
## Start Simple
### 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
**Default to minimal implementations:**
- New features should be <100 lines of code initially
- Use the simplest solution that works
- Avoid premature optimization (no caching, parallelization, or complex patterns without proven need)
- Choose boring technology over cutting-edge solutions
Triggers (examples):
- "Help me create a change proposal"
- "Help me plan a change"
- "Help me create a proposal"
- "I want to create a spec proposal"
- "I want to create a spec"
**Complexity triggers** - Only add complexity when you have:
- **Performance data** showing current solution is too slow
- **Scale requirements** with specific numbers (>1000 users, >100MB data)
- **Multiple use cases** requiring the same abstraction
- **Regulatory compliance** mandating specific patterns
- **Security threats** that simple solutions cannot address
Loose matching guidance:
- Contains one of: \`proposal\`, \`change\`, \`spec\`
- With one of: \`create\`, \`plan\`, \`make\`, \`start\`, \`help\`
Skip proposal for:
- Bug fixes (restore intended behavior)
- Typos, formatting, comments
- Dependency updates (non-breaking)
- Configuration changes
- Tests for existing behavior
### 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
6. **Validate strictly** - Run \`openspec validate [change] --strict\` and address issues
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
## Before Any Task
**Context Checklist:**
- [ ] Read relevant specs in \`specs/[capability]/spec.md\`
- [ ] Check pending changes in \`changes/\` for conflicts
- [ ] Read \`openspec/project.md\` for conventions
- [ ] Run \`openspec list\` to see active changes
- [ ] Run \`openspec list --specs\` to see existing capabilities
**Before Creating Specs:**
- Always check if capability already exists
- Prefer modifying existing specs over creating duplicates
- Use \`openspec show [spec]\` to review current state
- If request is ambiguous, ask 1–2 clarifying questions before scaffolding
### Search Guidance
- Enumerate specs: \`openspec spec list --long\` (or \`--json\` for scripts)
- Enumerate changes: \`openspec list\` (or \`openspec change list --json\` - deprecated but available)
- Show details:
- Spec: \`openspec show <spec-id> --type spec\` (use \`--json\` for filters)
- Change: \`openspec show <change-id> --json --deltas-only\`
- Full-text search (use ripgrep): \`rg -n "Requirement:|Scenario:" openspec/specs\`
## Quick Start
### CLI Commands
\`\`\`bash
# Essential commands
openspec list # List active changes
openspec list --specs # List specifications
openspec show [item] # Display change or spec
openspec 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
When triggered, document the specific justification in your change proposal.
## Directory Structure
\`\`\`
openspec/
├── project.md # Project conventions
├── project.md # Project-specific context (tech stack, conventions)
├── README.md # This file - OpenSpec instructions
├── specs/ # Current truth - what IS built
│ └── [capability]/ # Single focused capability
│ ├── spec.md # Requirements and scenarios
│ └── design.md # Technical patterns
├── changes/ # Proposals - what SHOULD change
│ ├── [capability]/ # Single, focused capability
│ │ ├── spec.md # WHAT the capability does and WHY
│ │ └── design.md # HOW it's built (established patterns)
│ └── ...
├── changes/ # Proposed changes - what we're CHANGING
│ ├── [change-name]/
│ │ ├── proposal.md # Why, what, impact
│ │ ├── proposal.md # Why, what, impact (consolidated)
│ │ ├── tasks.md # Implementation checklist
│ │ ├── design.md # Technical decisions (optional; see criteria)
│ │ └── specs/ # Delta changes
│ │ ├── design.md # Technical decisions (optional, for complex changes)
│ │ └── specs/ # Delta changes to specs
│ │ └── [capability]/
│ │ └── spec.md # ADDED/MODIFIED/REMOVED
│ └── archive/ # Completed changes
│ │ └── spec.md # Delta format (ADDED/MODIFIED/REMOVED/RENAMED)
│ └── archive/ # Completed changes (dated)
\`\`\`
## Creating Change Proposals
### Capability Organization
### Decision Tree
**Use capabilities, not features** - Each directory under \`specs/\` represents a single, focused responsibility:
- **Verb-noun naming**: \`user-auth\`, \`payment-capture\`, \`order-checkout\`
- **10-minute rule**: Each capability should be understandable in <10 minutes
- **Single purpose**: If it needs "AND" to describe it, split it
Examples:
\`\`\`
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)
✅ GOOD: user-auth, user-sessions, payment-capture, payment-refunds
❌ BAD: users, payments, core, misc
\`\`\`
### Proposal Structure
## Key Behavioral Rules
1. **Create directory:** \`changes/[change-id]/\` (kebab-case, verb-led, unique)
### 1. Always Start by Reading
2. **Write proposal.md:**
\`\`\`markdown
## Why
[1-2 sentences on problem/opportunity]
Before any task:
1. **Read relevant specs** in \`specs/[capability]/spec.md\` to understand current state
2. **Check pending changes** in \`changes/\` directory for potential conflicts
3. **Read project.md** for project-specific conventions
## What Changes
- [Bullet list of changes]
- [Mark breaking changes with **BREAKING**]
### 2. When to Create Change Proposals
## Impact
- Affected specs: [list capabilities]
- Affected code: [key files/systems]
\`\`\`
**ALWAYS create a change proposal for:**
- New features or functionality
- Breaking changes (API changes, schema updates)
- Architecture changes or new patterns
- Performance optimizations that change behavior
- Security updates affecting auth/access patterns
- Any change requiring multiple steps or affecting multiple systems
**SKIP proposals for:**
- Bug fixes that restore intended behavior
- Typos, formatting, or comment updates
- Dependency updates (unless breaking)
- Configuration or environment variable changes
- Adding tests for existing behavior
- Documentation fixes
**Complexity assessment:**
- If your solution requires >100 lines of new code, justify the complexity
- If adding dependencies, frameworks, or architectural patterns, document why simpler alternatives won't work
- Default to single-file implementations until proven insufficient
### 3. Delta-Based Change Format
Changes use a delta format with clear sections:
3. **Create spec deltas:** \`specs/[capability]/spec.md\`
\`\`\`markdown
## ADDED Requirements
### Requirement: New Feature
The system SHALL provide...
[Complete requirement content in structured format]
#### Scenario: Success case
- **WHEN** user performs action
- **THEN** expected result
## MODIFIED Requirements
## MODIFIED Requirements
### Requirement: Existing Feature
[Complete modified requirement]
[Complete modified requirement (header must match current spec)]
## REMOVED Requirements
### Requirement: Old Feature
**Reason**: [Why removing]
**Migration**: [How to handle]
\`\`\`
If multiple capabilities are affected, create multiple delta files under \`changes/[change-id]/specs/<capability>/spec.md\`—one per capability.
**Reason for removal**: [Why removing]
**Migration path**: [How to handle existing usage]
4. **Create tasks.md:**
\`\`\`markdown
## 1. Implementation
- [ ] 1.1 Create database schema
- [ ] 1.2 Implement API endpoint
- [ ] 1.3 Add frontend component
- [ ] 1.4 Write tests
\`\`\`
5. **Create design.md when needed:**
Create \`design.md\` if any of the following apply; otherwise omit it:
- Cross-cutting change (multiple services/modules) or a new architectural pattern
- New external dependency or significant data model changes
- Security, performance, or migration complexity
- Ambiguity that benefits from technical decisions before coding
Minimal \`design.md\` skeleton:
\`\`\`markdown
## Context
[Background, constraints, stakeholders]
## Goals / Non-Goals
- Goals: [...]
- Non-Goals: [...]
## Decisions
- Decision: [What and why]
- Alternatives considered: [Options + rationale]
## Risks / Trade-offs
- [Risk] → Mitigation
## Migration Plan
[Steps, rollback]
## Open Questions
- [...]
\`\`\`
## Spec File Format
### Critical: Scenario Formatting
**CORRECT** (use #### headers):
\`\`\`markdown
#### Scenario: User login success
- **WHEN** valid credentials provided
- **THEN** return JWT token
\`\`\`
**WRONG** (don't use bullets or bold):
\`\`\`markdown
- **Scenario: User login** ❌
**Scenario**: User login ❌
### Scenario: User login ❌
\`\`\`
Every requirement MUST have at least one scenario.
### Requirement Wording
- Use SHALL/MUST for normative requirements (avoid should/may unless intentionally non-normative)
### Delta Operations
- \`## ADDED Requirements\` - New capabilities
- \`## MODIFIED Requirements\` - Changed behavior
- \`## REMOVED Requirements\` - Deprecated features
- \`## RENAMED Requirements\` - Name changes
Headers matched with \`trim(header)\` - whitespace ignored.
Example for RENAMED:
\`\`\`markdown
## RENAMED Requirements
- FROM: \`### Requirement: Login\`
- TO: \`### Requirement: User Authentication\`
- FROM: \`### Requirement: Old Name\`
- TO: \`### Requirement: New Name\`
\`\`\`
## Troubleshooting
Key rules:
- Headers are matched using \`normalize(header) = trim(header)\`
- Include complete requirements (not diffs)
- Use standard symbols in CLI output: + (added), ~ (modified), - (removed), → (renamed)
### Common Errors
### 4. Creating a Change Proposal
**"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
When a user requests a significant change:
\`\`\`bash
# Always use strict mode for comprehensive checks
openspec validate [change] --strict
# 1. Create the change directory
openspec/changes/[descriptive-name]/
# Debug delta parsing
openspec show [change] --json | jq '.deltas'
# 2. Generate proposal.md with all context
## Why
[1-2 sentences on the problem/opportunity]
# Check specific requirement
openspec show [spec] --json -r 1
## What Changes
[Bullet list of changes, including breaking changes]
## Impact
- Affected specs: [list capabilities that will change]
- Affected code: [list key files/systems]
# 3. Create delta specs for ALL affected capabilities
# - Store only the changes (not complete future state)
# - Use sections: ## ADDED, ## MODIFIED, ## REMOVED, ## RENAMED
# - Include complete requirements in their final form
# Example spec.md content:
# ## ADDED Requirements
# ### Requirement: Password Reset
# Users SHALL be able to reset passwords via email...
#
# ## MODIFIED Requirements
# ### Requirement: User Authentication
# [Complete modified requirement with new password reset hook]
specs/
└── [capability]/
└── spec.md # Contains delta sections
# 4. Create tasks.md with implementation steps
## 1. [Task Group]
- [ ] 1.1 [Specific task]
- [ ] 1.2 [Specific task]
# 5. For complex changes, add design.md
[Technical decisions and trade-offs]
\`\`\`
## Happy Path Script
### 5. The Change Lifecycle
\`\`\`bash
# 1) Explore current state
openspec spec list --long
openspec list
# Optional full-text search:
# rg -n "Requirement:|Scenario:" openspec/specs
# rg -n "^#|Requirement:" openspec/changes
1. **Propose** → Create change directory with delta-based documentation
2. **Review** → User reviews and approves the proposal
3. **Implement** → Follow the approved tasks.md (can be multiple PRs)
4. **Deploy** → User confirms deployment
5. **Update Specs** → Apply deltas to sync specs/ with new reality (IF the change affects system capabilities)
6. **Archive** → Move to \`changes/archive/YYYY-MM-DD-[name]/\`
# 2) Choose change id and scaffold
CHANGE=add-two-factor-auth
mkdir -p openspec/changes/$CHANGE/{specs/auth}
printf "## Why\\n...\\n\\n## What Changes\\n- ...\\n\\n## Impact\\n- ...\\n" > openspec/changes/$CHANGE/proposal.md
printf "## 1. Implementation\\n- [ ] 1.1 ...\\n" > openspec/changes/$CHANGE/tasks.md
### 6. Implementing Changes
# 3) Add deltas (example)
cat > openspec/changes/$CHANGE/specs/auth/spec.md << 'EOF'
## ADDED Requirements
### Requirement: Two-Factor Authentication
Users MUST provide a second factor during login.
When implementing an approved change:
1. Follow the tasks.md checklist exactly
2. **Mark completed tasks** in tasks.md as you finish them (e.g., \`- [x] 1.1 Task completed\`)
3. Ensure code matches the proposed behavior
4. Update any affected tests
5. **Keep change in \`changes/\` directory** - do NOT archive in implementation PR
#### Scenario: OTP required
- **WHEN** valid credentials are provided
- **THEN** an OTP challenge is required
EOF
**Multiple Implementation PRs:**
- Changes can be implemented across multiple PRs
- Each PR should update tasks.md to mark what was completed
- Different developers can work on different task groups
- Example: PR #1 completes tasks 1.1-1.3, PR #2 completes tasks 2.1-2.4
# 4) Validate
openspec validate $CHANGE --strict
\`\`\`
### 7. Updating Specs and Archiving After Deployment
## Multi-Capability Example
**Create a separate PR after deployment** that:
1. Moves change to \`changes/archive/YYYY-MM-DD-[name]/\`
2. Updates relevant files in \`specs/\` to reflect new reality (if needed)
3. If design.md exists, incorporates proven patterns into \`specs/[capability]/design.md\`
\`\`\`
openspec/changes/add-2fa-notify/
├── proposal.md
├── tasks.md
└── specs/
├── auth/
│ └── spec.md # ADDED: Two-Factor Authentication
└── notifications/
└── spec.md # ADDED: OTP email notification
\`\`\`
This ensures changes are only archived when truly complete and deployed.
auth/spec.md
### 8. Types of Changes That Don't Require Specs
Some changes only affect development infrastructure and don't need specs:
- Initial project setup (package.json, tsconfig.json, etc.)
- Development tooling changes (linters, formatters, build tools)
- CI/CD configuration
- Development dependencies
For these changes:
1. Implement → Deploy → Mark tasks complete → Archive
2. Skip the "Update Specs" step entirely
### What Deserves a Spec?
Ask yourself:
- Is this a system capability that users or other systems interact with?
- Does it have ongoing behavior that needs documentation?
- Would a new developer need to understand this to work with the system?
If NO to all → No spec needed (likely just tooling/infrastructure)
## Understanding Specs vs Code
### Specs Document WHAT and WHY
\`\`\`markdown
## ADDED Requirements
### Requirement: Two-Factor Authentication
...
# Authentication Spec
Users SHALL authenticate with email and password.
WHEN credentials are valid THEN issue JWT token.
WHEN credentials are invalid THEN return generic error.
WHY: Prevent user enumeration attacks.
\`\`\`
notifications/spec.md
\`\`\`markdown
## ADDED Requirements
### Requirement: OTP Email Notification
...
### Code Documents HOW
\`\`\`javascript
// Implementation details
const user = await db.users.findOne({ email });
const valid = await bcrypt.compare(password, user.hashedPassword);
\`\`\`
## Best Practices
**Key Distinction**: Specs capture intent, constraints, and decisions that aren't obvious from code.
### Simplicity First
- Default to <100 lines of new code
- Single-file implementations until proven insufficient
- Avoid frameworks without clear justification
- Choose boring, proven patterns
## Common Scenarios
### 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
### New Feature Request
\`\`\`
User: "Add password reset functionality"
### Clear References
- Use \`file.ts:42\` format for code locations
- Reference specs as \`specs/auth/spec.md\`
- Link related changes and PRs
### Capability Naming
- Use verb-noun: \`user-auth\`, \`payment-capture\`
- Single purpose per capability
- 10-minute understandability rule
- Split if description needs "AND"
### Change ID Naming
- Use kebab-case, short and descriptive: \`add-two-factor-auth\`
- Prefer verb-led prefixes: \`add-\`, \`update-\`, \`remove-\`, \`refactor-\`
- Ensure uniqueness; if taken, append \`-2\`, \`-3\`, etc.
## Tool Selection Guide
| Task | Tool | Why |
|------|------|-----|
| Find files by pattern | Glob | Fast pattern matching |
| Search code content | Grep | Optimized regex search |
| Read specific files | Read | Direct file access |
| Explore unknown scope | Task | Multi-step investigation |
## Error Recovery
### Change Conflicts
1. Run \`openspec list\` to see active changes
2. Check for overlapping specs
3. Coordinate with change owners
4. Consider combining proposals
### Validation Failures
1. Run with \`--strict\` flag
2. Check JSON output for details
3. Verify spec file format
4. Ensure scenarios properly formatted
### Missing Context
1. Read project.md first
2. Check related specs
3. Review recent archives
4. Ask for clarification
## Quick Reference
### Stage Indicators
- \`changes/\` - Proposed, not yet built
- \`specs/\` - Built and deployed
- \`archive/\` - Completed changes
### File Purposes
- \`proposal.md\` - Why and what
- \`tasks.md\` - Implementation steps
- \`design.md\` - Technical decisions
- \`spec.md\` - Requirements and behavior
### CLI Essentials
\`\`\`bash
openspec list # What's in progress?
openspec show [item] # View details
openspec diff [change] # What's changing?
openspec validate --strict # Is it correct?
openspec archive [change] # Mark complete
You should:
1. Read specs/user-auth/spec.md
2. Check changes/ for pending auth changes
3. Create changes/add-password-reset/ with:
- proposal.md describing the change
- specs/user-auth/spec.md with:
## ADDED Requirements
### Requirement: Password Reset
[Complete requirement for password reset]
## MODIFIED Requirements
### Requirement: User Authentication
[Updated to integrate with password reset]
4. Wait for approval before implementing
\`\`\`
Remember: Specs are truth. Changes are proposals. Keep them in sync.
`;
### Bug Fix
\`\`\`
User: "Getting null pointer error when bio is empty"
You should:
1. Check if spec says bios are optional
2. If yes → Fix directly (it's a bug)
3. If no → Create change proposal (it's a behavior change)
\`\`\`
### Infrastructure Setup
\`\`\`
User: "Initialize TypeScript project"
You should:
1. Create change proposal for TypeScript setup
2. Implement configuration files (PR #1)
3. Mark tasks complete in tasks.md
4. After deployment, create separate PR to archive
(no specs update needed - this is tooling, not a capability)
\`\`\`
## Summary Workflow
1. **Receive request** → Determine if it needs a change proposal
2. **Read current state** → Check specs and pending changes
3. **Create proposal** → Generate complete change documentation
4. **Get approval** → User reviews the proposal
5. **Implement** → Follow approved tasks, mark completed items in tasks.md
6. **Deploy** → User deploys the implementation
7. **Archive PR** → Create separate PR to:
- Move change to archive
- Update specs if needed
- Mark change as complete
## PR Workflow Examples
### Single Developer, Simple Change
\`\`\`
PR #1: Implementation
- Implement all tasks
- Update tasks.md marking items complete
- Get merged and deployed
PR #2: Archive (after deployment)
- Move changes/feature-x/ → changes/archive/2025-01-15-feature-x/
- Update specs if needed
\`\`\`
### Multiple Developers, Complex Change
\`\`\`
PR #1: Alice implements auth components
- Complete tasks 1.1, 1.2, 1.3
- Update tasks.md marking these complete
PR #2: Bob implements UI components
- Complete tasks 2.1, 2.2
- Update tasks.md marking these complete
PR #3: Alice fixes integration issues
- Complete remaining task 1.4
- Update tasks.md
[Deploy all changes]
PR #4: Archive
- Move to archive with deployment date
- Update specs to reflect new auth flow
\`\`\`
### Key Rules
- **Never archive in implementation PRs** - changes aren't done until deployed
- **Always update tasks.md** - shows accurate progress
- **One archive PR per change** - clear completion boundary
- **Archive PR includes spec updates** - keeps specs current
## Capability Organization Best Practices
### Naming Capabilities
- Use **verb-noun** patterns: \`user-auth\`, \`payment-capture\`, \`order-checkout\`
- Be specific: \`payment-capture\` not just \`payments\`
- Keep flat: Avoid nesting capabilities within capabilities
- Singular focus: If you need "AND" to describe it, split it
### When to Split Capabilities
Split when you have:
- Multiple unrelated API endpoints
- Different user personas or actors
- Separate deployment considerations
- Independent evolution paths
#### Capability Boundary Guidelines
- Would you import these separately? → Separate capabilities
- Different deployment cadence? → Separate capabilities
- Different teams own them? → Separate capabilities
- Shared data models are OK, shared business logic means combine
Examples:
- user-auth (login/logout) vs user-sessions (token management) → SEPARATE
- payment-capture vs payment-refunds → SEPARATE (different workflows)
- user-profile vs user-settings → COMBINE (same data model, same owner)
### Cross-Cutting Concerns
For system-wide policies (rate limiting, error handling, security), document them in:
- \`project.md\` for project-wide conventions
- Within relevant capability specs where they apply
- Or create a dedicated capability if complex enough (e.g., \`api-rate-limiting/\`)
### Examples of Well-Organized Capabilities
\`\`\`
specs/
├── user-auth/ # Login, logout, password reset
├── user-sessions/ # Token management, refresh
├── user-profile/ # Profile CRUD operations
├── payment-capture/ # Processing payments
├── payment-refunds/ # Handling refunds
└── order-checkout/ # Checkout workflow
\`\`\`
For detailed guidance, see the [Capability Organization Guide](../docs/capability-organization.md).
## Common Scenarios and Clarifications
### Decision Ambiguity: Bug vs Behavior Change
When specs are missing or ambiguous:
- If NO spec exists → Treat current code behavior as implicit spec, require proposal
- If spec is VAGUE → Require proposal to clarify spec alongside fix
- If code and spec DISAGREE → Spec is truth, code is buggy (fix without proposal)
- If unsure → Default to creating a proposal (safer option)
Example:
\`\`\`
User: "The API returns 404 for missing users but should return 400"
AI: Is this a bug (spec says 400) or behavior change (spec says 404)?
\`\`\`
### When You Don't Know the Scope
It's OK to explore first! Tell the user you need to investigate, then create an informed proposal.
### Exploration Phase (When Needed)
BEFORE creating proposal, you may need exploration when:
- User request is vague or high-level
- Multiple implementation approaches exist
- Scope is unclear without seeing code
Exploration checklist:
1. Tell user you need to explore first
2. Use Grep/Read to understand current state
3. Create initial proposal based on findings
4. Refine with user feedback
Example:
\`\`\`
User: "Add caching to improve performance"
AI: "Let me explore the codebase to understand the current architecture and identify caching opportunities."
[After exploration]
AI: "Based on my analysis, I've identified three areas where caching would help. Here's my proposal..."
\`\`\`
### When No Specs Exist
Treat current code as implicit spec. Your proposal should document current state AND proposed changes.
### When in Doubt
Default to creating a proposal. It's easier to skip an unnecessary proposal than fix an undocumented change.
### AI Workflow Adaptations
Task tracking with OpenSpec:
- Track exploration tasks separately from implementation
- Document proposal creation steps as you go
- Keep implementation tasks separate until proposal approved
Parallel operations encouraged:
- Read multiple specs simultaneously
- Check multiple pending changes at once
- Batch related searches for efficiency
Progress communication:
- "Exploring codebase to understand scope..."
- "Creating proposal based on findings..."
- "Implementing approved changes..."
### For AI Assistants
- **Bias toward simplicity** - Propose the minimal solution that works
- Use your exploration tools liberally before proposing
- Batch operations for efficiency
- Communicate your progress
- It's OK to revise proposals based on discoveries
- **Question complexity** - If your solution feels complex, simplify first
## Edge Case Handling
### Multi-Capability Changes
Create ONE proposal that:
- Lists all affected capabilities
- Shows changes per capability
- Has unified task list
- Gets approved as a whole
### Outdated Specs
If specs clearly outdated:
1. Create proposal to update specs to match reality
2. Implement new feature in separate proposal
3. OR combine both in one proposal with clear sections
### Emergency Hotfixes
For critical production issues:
1. Announce: "This is an emergency fix"
2. Implement fix immediately
3. Create retroactive proposal
4. Update specs after deployment
5. Tag with [EMERGENCY] in archive
### Pure Refactoring
No proposal needed for:
- Code formatting/style
- Internal refactoring (same API)
- Performance optimization (same behavior)
- Adding types to untyped code
Proposal REQUIRED for:
- API changes (even if compatible)
- Database schema changes
- Architecture changes
- New dependencies
### Observability Additions
No proposal needed for:
- Adding log statements
- New metrics/traces
- Debugging additions
- Error tracking
Proposal REQUIRED if:
- Changes log format/structure
- Adds new monitoring service
- Changes what's logged (privacy)
## Remember
- You are the process driver - automate documentation burden
- Specs must always reflect deployed reality
- Changes are proposed, not imposed
- Impact analysis prevents surprises
- Simplicity is the power - just markdown files, minimal solutions
- Start simple, add complexity only when justified
By following these conventions, you enable true spec-driven development where documentation stays current, changes are traceable, and evolution is intentional.
`;