@fission-ai/openspec 0.1.0 β†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 OpenSpec Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md CHANGED
@@ -1,119 +1,283 @@
1
+ <p align="center">
2
+ <a href="https://github.com/Fission-AI/OpenSpec">
3
+ <picture>
4
+ <source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
5
+ <source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
6
+ <img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
7
+ </picture>
8
+ </a>
9
+
10
+ </p>
11
+ <p align="center">Spec-driven development for AI coding assistants.</p>
12
+ <p align="center">
13
+ <a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
14
+ <a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
15
+ <a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
16
+ <a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
17
+ <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>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
22
+ </p>
23
+
24
+ <p align="center">
25
+ Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates.
26
+ </p>
27
+
1
28
  # OpenSpec
2
29
 
3
- A specification-driven development system for maintaining living documentation alongside your code.
30
+ **Supported AI Tools:** βœ… Claude Code | πŸ”œ Cursor (coming soon) | βœ… AGENTS.md instructions
31
+
32
+ Create **alignment** between humans and AI coding assistants through spec-driven development. **No API keys required.**
33
+
34
+ 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.
35
+
36
+ ## Why OpenSpec?
37
+
38
+ **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.
39
+
40
+ **The Solution:** OpenSpec creates alignment BEFORE code is written:
41
+ - **Human-AI Alignment** - You and your AI agree on specifications before implementation
42
+ - **Deterministic, Predictable Output** - Clear specs lead to reliable, repeatable code generation
43
+ - **Team Alignment via Spec Reviews** - Everyone reviews intentions, not code surprises
44
+ - **Clear Feature Scope** - Know exactly what you're buildingβ€”and what you're not
45
+ - **Progress Tracking** - See what's proposed, in progress, or completed at a glance
46
+ - **Living Documentation** - Specs evolve with your code as a natural byproduct
47
+ - **Universal Tool Support** - Works with any AI assistant (Claude Code, Cursor, and more)
48
+ - **No API Keys Required** - Integrates through context rules, not external services
49
+
50
+ ## How It Works
51
+
52
+ ```
53
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
54
+ β”‚ SPECS β”‚ β”‚ CHANGES β”‚ β”‚ ARCHIVE β”‚
55
+ β”‚ (Truth) │◀──────│ (Proposals) │──────▢│ (Completed) β”‚
56
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
57
+ β–² β”‚ β”‚
58
+ β”‚ β–Ό β”‚
59
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
60
+ └───────────────│ CODE β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
61
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
62
+
63
+ 1. SPECS define current capabilities (what IS built)
64
+ 2. CHANGES propose modifications using deltas (what SHOULD change)
65
+ 3. CODE implements the changes following tasks
66
+ 4. ARCHIVE preserves completed changes after deployment
67
+ ```
4
68
 
5
69
  ## Installation
6
70
 
71
+ ### Prerequisites
72
+
73
+ - Node.js >= 20.19.0
74
+
75
+ ### Install OpenSpec
76
+
77
+ Install globally:
78
+
7
79
  ```bash
8
- npm install -g openspec
80
+ npm install -g @fission-ai/openspec
9
81
  ```
10
82
 
11
- ## Quick Start
83
+ ## Getting Started
84
+
85
+ ### 1. Initialize OpenSpec in Your Project
12
86
 
13
87
  ```bash
14
- # Initialize OpenSpec in your project
88
+ # Navigate to your project
89
+ cd my-project
90
+
91
+ # Initialize OpenSpec
15
92
  openspec init
16
93
 
17
- # Update existing OpenSpec instructions (team-friendly)
18
- openspec update
94
+ # Select your AI tool (more coming soon!):
95
+ # "Which AI tool do you use?"
96
+ # > Claude Code
97
+ # Cursor (coming soon)
98
+
99
+ # This creates:
100
+ # openspec/
101
+ # β”œβ”€β”€ specs/ # Current specifications (truth)
102
+ # β”œβ”€β”€ changes/ # Proposed changes
103
+ # └── AGENTS.md # AI instructions for your tool
104
+ ```
105
+
106
+ ### 2. Create Your First Change
107
+
108
+ Jump straight into creating a change proposal with your AI assistant (works with Claude Code, Cursor, or any AI tool):
109
+
110
+ ```markdown
111
+ // Quick win - Add a simple new feature:
112
+ You: "I want to add a user profile API endpoint.
113
+ Please create an OpenSpec change proposal for this."
19
114
 
20
- # List specs or changes
21
- openspec spec list # specs (IDs by default; use --long for details)
22
- openspec change list # changes (IDs by default; use --long for details)
115
+ AI: "I'll create an OpenSpec change proposal for the user profile API..."
116
+ *Creates openspec/changes/add-user-profile-api/ with:*
117
+ - proposal.md (why this feature is needed)
118
+ - tasks.md (implementation checklist)
119
+ - design.md (API design decisions)
120
+ - specs/user-profile/spec.md (new requirements)
23
121
 
24
- # Show differences between specs and proposed changes
25
- openspec diff [change-name]
122
+ You: "The proposal looks good. Let's implement it."
26
123
 
27
- # Archive completed changes
28
- openspec archive [change-name]
124
+ AI: "Following the tasks in openspec/changes/add-user-profile-api/tasks.md:
125
+ Task 1.1: Create user profile model..."
126
+ *Implements each task systematically*
29
127
  ```
30
128
 
31
- ## Commands
129
+ ### 3. Track Your Work
32
130
 
33
- ### `openspec init`
131
+ ```bash
132
+ # View active changes (what's being worked on)
133
+ openspec list
34
134
 
35
- Initializes OpenSpec in your project by creating:
36
- - `openspec/` directory structure
37
- - `openspec/README.md` with OpenSpec instructions
38
- - AI tool configuration files (based on your selection)
135
+ # Validate your changes are properly formatted
136
+ openspec validate add-2fa --strict
39
137
 
40
- ### `openspec update`
138
+ # After deployment, archive the completed change
139
+ openspec archive add-2fa
140
+ # This moves the change to archive/ and updates specs/
141
+ ```
41
142
 
42
- Updates OpenSpec instructions to the latest version. This command is **team-friendly** and only updates files that already exist:
143
+ ## Common Commands
43
144
 
44
- - Always updates `openspec/README.md` with the latest OpenSpec instructions
45
- - **Only updates existing AI tool configuration files** (e.g., CLAUDE.md, CURSOR.md)
46
- - **Never creates new AI tool configuration files**
47
- - Preserves content outside of OpenSpec markers in AI tool files
145
+ ```bash
146
+ # Most used:
147
+ openspec list # See what changes you're working on
148
+ openspec archive <change> # Mark a change as complete after deployment
48
149
 
49
- This allows team members to use different AI tools without conflicts. Each developer can maintain their preferred AI tool configuration file, and `openspec update` will respect their choice.
150
+ # Also useful:
151
+ openspec validate <change> # Check formatting before committing
152
+ openspec show <change> # View change details
153
+ ```
50
154
 
51
- ### `openspec spec`
155
+ ## Example: How AI Creates OpenSpec Files
52
156
 
53
- Manage and view specifications.
157
+ When you ask your AI assistant to "add two-factor authentication", it creates:
54
158
 
55
- Examples:
56
- - `openspec spec show <spec-id>`
57
- - Text mode: prints raw `spec.md` content
58
- - JSON mode (`--json`): returns minimal, stable shape
59
- - Filters are JSON-only: `--requirements`, `--no-scenarios`, `-r/--requirement <1-based>`
60
- - `openspec spec list`
61
- - Prints IDs only by default
62
- - Use `--long` to include `title` and `[requirements N]`
63
- - `openspec spec validate <spec-id>`
64
- - Text: human-readable summary to stdout/stderr
65
- - `--json` for structured report
159
+ ```
160
+ openspec/
161
+ β”œβ”€β”€ specs/
162
+ β”‚ └── auth/
163
+ β”‚ └── spec.md # Current auth spec (if exists)
164
+ └── changes/
165
+ └── add-2fa/ # AI creates this entire structure
166
+ β”œβ”€β”€ proposal.md # Why and what changes
167
+ β”œβ”€β”€ tasks.md # Implementation checklist
168
+ β”œβ”€β”€ design.md # Technical decisions (optional)
169
+ └── specs/
170
+ └── auth/
171
+ └── spec.md # Delta showing additions
172
+ ```
66
173
 
67
- ### `openspec change`
174
+ ### AI-Generated Spec (created in `openspec/specs/auth/spec.md`):
68
175
 
69
- Manage and view change proposals.
176
+ ```markdown
177
+ # Auth Specification
70
178
 
71
- Examples:
72
- - `openspec change show <change-id>`
73
- - Text mode: prints raw `proposal.md` content
74
- - JSON mode (`--json`): `{ id, title, deltaCount, deltas }`
75
- - Filtering is JSON-only: `--deltas-only` (alias: `--requirements-only`, deprecated)
76
- - `openspec change list`
77
- - Prints IDs only by default
78
- - Use `--long` to include `title` and counts `[deltas N] [tasks x/y]`
79
- - `openspec change validate <change-id>`
80
- - Text: human-readable result
81
- - `--json` for structured report
179
+ ## Purpose
180
+ Authentication and session management.
82
181
 
83
- ### `openspec diff [change-name]`
182
+ ## Requirements
183
+ ### Requirement: User Authentication
184
+ The system SHALL issue a JWT on successful login.
84
185
 
85
- Shows the differences between current specs and proposed changes:
86
- - Displays a unified diff format
87
- - Helps review what will change before implementation
88
- - Useful for pull request reviews
186
+ #### Scenario: Valid credentials
187
+ - WHEN a user submits valid credentials
188
+ - THEN a JWT is returned
189
+ ```
190
+
191
+ ### AI-Generated Change Delta (created in `openspec/changes/add-2fa/specs/auth/spec.md`):
89
192
 
90
- ### `openspec archive [change-name]`
193
+ ```markdown
194
+ # Delta for Auth
91
195
 
92
- Archives a completed change:
93
- - Moves change from `openspec/changes/` to `openspec/changes/archive/`
94
- - Adds a date prefix to the archived change
95
- - Updates specs to reflect the new state
96
- - Use `--skip-specs` to archive without updating specs (for abandoned changes)
196
+ ## ADDED Requirements
197
+ ### Requirement: Two-Factor Authentication
198
+ The system MUST require a second factor during login.
97
199
 
98
- ## Team Collaboration
200
+ #### Scenario: OTP required
201
+ - WHEN a user submits valid credentials
202
+ - THEN an OTP challenge is required
203
+ ```
99
204
 
100
- OpenSpec is designed for team collaboration:
205
+ ### AI-Generated Tasks (created in `openspec/changes/add-2fa/tasks.md`):
101
206
 
102
- 1. **AI Tool Flexibility**: Each team member can use their preferred AI assistant (Claude, Cursor, etc.)
103
- 2. **Non-Invasive Updates**: The `update` command only modifies existing files, never forcing tools on team members
104
- 3. **Specification Sharing**: The `openspec/` directory contains shared specifications that all team members work from
105
- 4. **Change Tracking**: Proposed changes are visible to all team members for review before implementation
207
+ ```markdown
208
+ ## 1. Database Setup
209
+ - [ ] 1.1 Add OTP secret column to users table
210
+ - [ ] 1.2 Create OTP verification logs table
211
+
212
+ ## 2. Backend Implementation
213
+ - [ ] 2.1 Add OTP generation endpoint
214
+ - [ ] 2.2 Modify login flow to require OTP
215
+ - [ ] 2.3 Add OTP verification endpoint
216
+
217
+ ## 3. Frontend Updates
218
+ - [ ] 3.1 Create OTP input component
219
+ - [ ] 3.2 Update login flow UI
220
+ ```
221
+
222
+ **Important:** You don't create these files manually. Your AI assistant generates them based on your requirements and the existing codebase.
223
+
224
+ ## Understanding OpenSpec Files
225
+
226
+ ### Delta Format
227
+
228
+ Deltas are "patches" that show how specs change:
229
+
230
+ - **`## ADDED Requirements`** - New capabilities
231
+ - **`## MODIFIED Requirements`** - Changed behavior (include complete updated text)
232
+ - **`## REMOVED Requirements`** - Deprecated features
233
+
234
+ **Format requirements:**
235
+ - Use `### Requirement: <name>` for headers
236
+ - Every requirement needs at least one `#### Scenario:` block
237
+ - Use SHALL/MUST in requirement text
106
238
 
107
- ## Contributing
108
239
 
109
- See `openspec/specs/` for the current system specifications and `openspec/changes/` for pending improvements.
240
+ ## Why OpenSpec Works
110
241
 
111
- ## Notes
242
+ OpenSpec creates **alignment** between you and your AI coding assistant:
243
+
244
+ 1. **You describe** what you want to build
245
+ 2. **AI creates specs** before writing any code
246
+ 3. **You review and adjust** the specifications
247
+ 4. **AI implements** exactly what was specified
248
+ 5. **Everyone understands** what's being built through clear specs
249
+
250
+ **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.
251
+
252
+
253
+ ## How OpenSpec Compares
254
+
255
+ ### vs. Kiro.dev
256
+ 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.
257
+
258
+ ### vs. No Specs
259
+ 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.
260
+
261
+ ## Team Adoption
262
+
263
+ ### Getting Started with Your Team
264
+
265
+ 1. **Initialize OpenSpec** - Run `openspec init` in your project
266
+ 2. **Start with new features** - Use OpenSpec for your next change proposal
267
+ 3. **Build incrementally** - Each new feature adds to your spec library
268
+ 4. **Future capability** - We're working on tools to generate specs from existing code
269
+
270
+ **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.
271
+
272
+
273
+ ## Contributing
112
274
 
113
- - The legacy `openspec list` command is deprecated. Use `openspec spec list` and `openspec change list`.
114
- - Text output is raw-first (no formatting or filtering). Prefer `--json` for tooling-friendly output.
115
- - Global `--no-color` disables ANSI colors and respects `NO_COLOR`.
275
+ - Install dependencies: `npm install`
276
+ - Build: `npm run build`
277
+ - Test: `npm test`
278
+ - Develop CLI locally: `npm run dev` or `npm run dev:cli`
279
+ - Conventional commits (one-line): `type(scope): subject`
116
280
 
117
281
  ## License
118
282
 
119
- MIT
283
+ MIT
package/dist/cli/index.js CHANGED
@@ -5,9 +5,9 @@ import path from 'path';
5
5
  import { promises as fs } from 'fs';
6
6
  import { InitCommand } from '../core/init.js';
7
7
  import { UpdateCommand } from '../core/update.js';
8
- import { DiffCommand } from '../core/diff.js';
9
8
  import { ListCommand } from '../core/list.js';
10
9
  import { ArchiveCommand } from '../core/archive.js';
10
+ import { ViewCommand } from '../core/view.js';
11
11
  import { registerSpecCommand } from '../commands/spec.js';
12
12
  import { ChangeCommand } from '../commands/change.js';
13
13
  import { ValidateCommand } from '../commands/validate.js';
@@ -78,12 +78,15 @@ program
78
78
  }
79
79
  });
80
80
  program
81
- .command('diff [change-name]')
82
- .description('Show differences between proposed spec changes and current specs (includes validation warnings)')
83
- .action(async (changeName) => {
81
+ .command('list')
82
+ .description('List items (changes by default). Use --specs to list specs.')
83
+ .option('--specs', 'List specs instead of changes')
84
+ .option('--changes', 'List changes explicitly (default)')
85
+ .action(async (options) => {
84
86
  try {
85
- const diffCommand = new DiffCommand();
86
- await diffCommand.execute(changeName);
87
+ const listCommand = new ListCommand();
88
+ const mode = options?.specs ? 'specs' : 'changes';
89
+ await listCommand.execute('.', mode);
87
90
  }
88
91
  catch (error) {
89
92
  console.log(); // Empty line for spacing
@@ -92,15 +95,12 @@ program
92
95
  }
93
96
  });
94
97
  program
95
- .command('list')
96
- .description('List items (changes by default). Use --specs to list specs.')
97
- .option('--specs', 'List specs instead of changes')
98
- .option('--changes', 'List changes explicitly (default)')
99
- .action(async (options) => {
98
+ .command('view')
99
+ .description('Display an interactive dashboard of specs and changes')
100
+ .action(async () => {
100
101
  try {
101
- const listCommand = new ListCommand();
102
- const mode = options?.specs ? 'specs' : 'changes';
103
- await listCommand.execute('.', mode);
102
+ const viewCommand = new ViewCommand();
103
+ await viewCommand.execute('.');
104
104
  }
105
105
  catch (error) {
106
106
  console.log(); // Empty line for spacing
@@ -5,7 +5,7 @@ export const OPENSPEC_MARKERS = {
5
5
  };
6
6
  export const AI_TOOLS = [
7
7
  { name: 'Claude Code', value: 'claude', available: true },
8
- { name: 'Cursor', value: 'cursor', available: false },
8
+ { name: 'Cursor', value: 'cursor', available: true },
9
9
  { name: 'Aider', value: 'aider', available: false },
10
10
  { name: 'Continue', value: 'continue', available: false }
11
11
  ];
@@ -0,0 +1,17 @@
1
+ import { SlashCommandId } from '../../templates/index.js';
2
+ export interface SlashCommandTarget {
3
+ id: SlashCommandId;
4
+ path: string;
5
+ kind: 'slash';
6
+ }
7
+ export declare abstract class SlashCommandConfigurator {
8
+ abstract readonly toolId: string;
9
+ abstract readonly isAvailable: boolean;
10
+ getTargets(): SlashCommandTarget[];
11
+ generateAll(projectPath: string, _openspecDir: string): Promise<string[]>;
12
+ updateExisting(projectPath: string, _openspecDir: string): Promise<string[]>;
13
+ protected abstract getRelativePath(id: SlashCommandId): string;
14
+ protected abstract getFrontmatter(id: SlashCommandId): string | undefined;
15
+ private updateBody;
16
+ }
17
+ //# sourceMappingURL=base.d.ts.map
@@ -0,0 +1,61 @@
1
+ import path from 'path';
2
+ import { FileSystemUtils } from '../../../utils/file-system.js';
3
+ import { TemplateManager } from '../../templates/index.js';
4
+ import { OPENSPEC_MARKERS } from '../../config.js';
5
+ const ALL_COMMANDS = ['proposal', 'apply', 'archive'];
6
+ export class SlashCommandConfigurator {
7
+ getTargets() {
8
+ return ALL_COMMANDS.map((id) => ({
9
+ id,
10
+ path: this.getRelativePath(id),
11
+ kind: 'slash'
12
+ }));
13
+ }
14
+ async generateAll(projectPath, _openspecDir) {
15
+ const createdOrUpdated = [];
16
+ for (const target of this.getTargets()) {
17
+ const body = TemplateManager.getSlashCommandBody(target.id).trim();
18
+ const filePath = path.join(projectPath, target.path);
19
+ if (await FileSystemUtils.fileExists(filePath)) {
20
+ await this.updateBody(filePath, body);
21
+ }
22
+ else {
23
+ const frontmatter = this.getFrontmatter(target.id);
24
+ const sections = [];
25
+ if (frontmatter) {
26
+ sections.push(frontmatter.trim());
27
+ }
28
+ sections.push(`${OPENSPEC_MARKERS.start}\n${body}\n${OPENSPEC_MARKERS.end}`);
29
+ const content = sections.join('\n') + '\n';
30
+ await FileSystemUtils.writeFile(filePath, content);
31
+ }
32
+ createdOrUpdated.push(target.path);
33
+ }
34
+ return createdOrUpdated;
35
+ }
36
+ async updateExisting(projectPath, _openspecDir) {
37
+ const updated = [];
38
+ for (const target of this.getTargets()) {
39
+ const filePath = path.join(projectPath, target.path);
40
+ if (await FileSystemUtils.fileExists(filePath)) {
41
+ const body = TemplateManager.getSlashCommandBody(target.id).trim();
42
+ await this.updateBody(filePath, body);
43
+ updated.push(target.path);
44
+ }
45
+ }
46
+ return updated;
47
+ }
48
+ async updateBody(filePath, body) {
49
+ const content = await FileSystemUtils.readFile(filePath);
50
+ const startIndex = content.indexOf(OPENSPEC_MARKERS.start);
51
+ const endIndex = content.indexOf(OPENSPEC_MARKERS.end);
52
+ if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
53
+ throw new Error(`Missing OpenSpec markers in ${filePath}`);
54
+ }
55
+ const before = content.slice(0, startIndex + OPENSPEC_MARKERS.start.length);
56
+ const after = content.slice(endIndex);
57
+ const updatedContent = `${before}\n${body}\n${after}`;
58
+ await FileSystemUtils.writeFile(filePath, updatedContent);
59
+ }
60
+ }
61
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1,9 @@
1
+ import { SlashCommandConfigurator } from './base.js';
2
+ import { SlashCommandId } from '../../templates/index.js';
3
+ export declare class ClaudeSlashCommandConfigurator extends SlashCommandConfigurator {
4
+ readonly toolId = "claude";
5
+ readonly isAvailable = true;
6
+ protected getRelativePath(id: SlashCommandId): string;
7
+ protected getFrontmatter(id: SlashCommandId): string;
8
+ }
9
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { SlashCommandConfigurator } from './base.js';
2
+ const FILE_PATHS = {
3
+ proposal: '.claude/commands/openspec/proposal.md',
4
+ apply: '.claude/commands/openspec/apply.md',
5
+ archive: '.claude/commands/openspec/archive.md'
6
+ };
7
+ const FRONTMATTER = {
8
+ proposal: `---
9
+ name: OpenSpec: Proposal
10
+ description: Scaffold a new OpenSpec change and validate strictly.
11
+ category: OpenSpec
12
+ tags: [openspec, change]
13
+ ---`,
14
+ apply: `---
15
+ name: OpenSpec: Apply
16
+ description: Implement an approved OpenSpec change and keep tasks in sync.
17
+ category: OpenSpec
18
+ tags: [openspec, apply]
19
+ ---`,
20
+ archive: `---
21
+ name: OpenSpec: Archive
22
+ description: Archive a deployed OpenSpec change and update specs.
23
+ category: OpenSpec
24
+ tags: [openspec, archive]
25
+ ---`
26
+ };
27
+ export class ClaudeSlashCommandConfigurator extends SlashCommandConfigurator {
28
+ toolId = 'claude';
29
+ isAvailable = true;
30
+ getRelativePath(id) {
31
+ return FILE_PATHS[id];
32
+ }
33
+ getFrontmatter(id) {
34
+ return FRONTMATTER[id];
35
+ }
36
+ }
37
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1,9 @@
1
+ import { SlashCommandConfigurator } from './base.js';
2
+ import { SlashCommandId } from '../../templates/index.js';
3
+ export declare class CursorSlashCommandConfigurator extends SlashCommandConfigurator {
4
+ readonly toolId = "cursor";
5
+ readonly isAvailable = true;
6
+ protected getRelativePath(id: SlashCommandId): string;
7
+ protected getFrontmatter(id: SlashCommandId): string;
8
+ }
9
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { SlashCommandConfigurator } from './base.js';
2
+ const FILE_PATHS = {
3
+ proposal: '.cursor/commands/openspec-proposal.md',
4
+ apply: '.cursor/commands/openspec-apply.md',
5
+ archive: '.cursor/commands/openspec-archive.md'
6
+ };
7
+ const FRONTMATTER = {
8
+ proposal: `---
9
+ name: /openspec-proposal
10
+ id: openspec-proposal
11
+ category: OpenSpec
12
+ description: Scaffold a new OpenSpec change and validate strictly.
13
+ ---`,
14
+ apply: `---
15
+ name: /openspec-apply
16
+ id: openspec-apply
17
+ category: OpenSpec
18
+ description: Implement an approved OpenSpec change and keep tasks in sync.
19
+ ---`,
20
+ archive: `---
21
+ name: /openspec-archive
22
+ id: openspec-archive
23
+ category: OpenSpec
24
+ description: Archive a deployed OpenSpec change and update specs.
25
+ ---`
26
+ };
27
+ export class CursorSlashCommandConfigurator extends SlashCommandConfigurator {
28
+ toolId = 'cursor';
29
+ isAvailable = true;
30
+ getRelativePath(id) {
31
+ return FILE_PATHS[id];
32
+ }
33
+ getFrontmatter(id) {
34
+ return FRONTMATTER[id];
35
+ }
36
+ }
37
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1,8 @@
1
+ import { SlashCommandConfigurator } from './base.js';
2
+ export declare class SlashCommandRegistry {
3
+ private static configurators;
4
+ static register(configurator: SlashCommandConfigurator): void;
5
+ static get(toolId: string): SlashCommandConfigurator | undefined;
6
+ static getAll(): SlashCommandConfigurator[];
7
+ }
8
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { ClaudeSlashCommandConfigurator } from './claude.js';
2
+ import { CursorSlashCommandConfigurator } from './cursor.js';
3
+ export class SlashCommandRegistry {
4
+ static configurators = new Map();
5
+ static {
6
+ const claude = new ClaudeSlashCommandConfigurator();
7
+ const cursor = new CursorSlashCommandConfigurator();
8
+ this.configurators.set(claude.toolId, claude);
9
+ this.configurators.set(cursor.toolId, cursor);
10
+ }
11
+ static register(configurator) {
12
+ this.configurators.set(configurator.toolId, configurator);
13
+ }
14
+ static get(toolId) {
15
+ return this.configurators.get(toolId);
16
+ }
17
+ static getAll() {
18
+ return Array.from(this.configurators.values());
19
+ }
20
+ }
21
+ //# sourceMappingURL=registry.js.map