superspec 0.1.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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +328 -0
  3. package/dist/cli/commands/archive.d.ts +7 -0
  4. package/dist/cli/commands/archive.d.ts.map +1 -0
  5. package/dist/cli/commands/archive.js +322 -0
  6. package/dist/cli/commands/archive.js.map +1 -0
  7. package/dist/cli/commands/init.d.ts +6 -0
  8. package/dist/cli/commands/init.d.ts.map +1 -0
  9. package/dist/cli/commands/init.js +306 -0
  10. package/dist/cli/commands/init.js.map +1 -0
  11. package/dist/cli/commands/list.d.ts +9 -0
  12. package/dist/cli/commands/list.d.ts.map +1 -0
  13. package/dist/cli/commands/list.js +268 -0
  14. package/dist/cli/commands/list.js.map +1 -0
  15. package/dist/cli/commands/show.d.ts +6 -0
  16. package/dist/cli/commands/show.d.ts.map +1 -0
  17. package/dist/cli/commands/show.js +273 -0
  18. package/dist/cli/commands/show.js.map +1 -0
  19. package/dist/cli/commands/validate.d.ts +8 -0
  20. package/dist/cli/commands/validate.d.ts.map +1 -0
  21. package/dist/cli/commands/validate.js +209 -0
  22. package/dist/cli/commands/validate.js.map +1 -0
  23. package/dist/cli/commands/verify.d.ts +7 -0
  24. package/dist/cli/commands/verify.d.ts.map +1 -0
  25. package/dist/cli/commands/verify.js +328 -0
  26. package/dist/cli/commands/verify.js.map +1 -0
  27. package/dist/cli/commands/view.d.ts +6 -0
  28. package/dist/cli/commands/view.d.ts.map +1 -0
  29. package/dist/cli/commands/view.js +290 -0
  30. package/dist/cli/commands/view.js.map +1 -0
  31. package/dist/cli/index.d.ts +3 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +87 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/cli/ui/display.d.ts +189 -0
  36. package/dist/cli/ui/display.d.ts.map +1 -0
  37. package/dist/cli/ui/display.js +449 -0
  38. package/dist/cli/ui/display.js.map +1 -0
  39. package/dist/cli/ui/index.d.ts +12 -0
  40. package/dist/cli/ui/index.d.ts.map +1 -0
  41. package/dist/cli/ui/index.js +71 -0
  42. package/dist/cli/ui/index.js.map +1 -0
  43. package/dist/cli/ui/interactive.d.ts +21 -0
  44. package/dist/cli/ui/interactive.d.ts.map +1 -0
  45. package/dist/cli/ui/interactive.js +60 -0
  46. package/dist/cli/ui/interactive.js.map +1 -0
  47. package/dist/cli/ui/palette.d.ts +301 -0
  48. package/dist/cli/ui/palette.d.ts.map +1 -0
  49. package/dist/cli/ui/palette.js +673 -0
  50. package/dist/cli/ui/palette.js.map +1 -0
  51. package/dist/cli/ui/prompts.d.ts +62 -0
  52. package/dist/cli/ui/prompts.d.ts.map +1 -0
  53. package/dist/cli/ui/prompts.js +119 -0
  54. package/dist/cli/ui/prompts.js.map +1 -0
  55. package/dist/cli/ui/spinner.d.ts +38 -0
  56. package/dist/cli/ui/spinner.d.ts.map +1 -0
  57. package/dist/cli/ui/spinner.js +72 -0
  58. package/dist/cli/ui/spinner.js.map +1 -0
  59. package/dist/core/config/project-config.d.ts +100 -0
  60. package/dist/core/config/project-config.d.ts.map +1 -0
  61. package/dist/core/config/project-config.js +87 -0
  62. package/dist/core/config/project-config.js.map +1 -0
  63. package/dist/core/parsers/spec-parser.d.ts +48 -0
  64. package/dist/core/parsers/spec-parser.d.ts.map +1 -0
  65. package/dist/core/parsers/spec-parser.js +322 -0
  66. package/dist/core/parsers/spec-parser.js.map +1 -0
  67. package/dist/core/validation/spec-validator.d.ts +32 -0
  68. package/dist/core/validation/spec-validator.d.ts.map +1 -0
  69. package/dist/core/validation/spec-validator.js +242 -0
  70. package/dist/core/validation/spec-validator.js.map +1 -0
  71. package/dist/index.d.ts +35 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +44 -0
  74. package/dist/index.js.map +1 -0
  75. package/package.json +66 -0
  76. package/schemas/superspec-workflow/schema.yaml +166 -0
  77. package/templates/design.md +71 -0
  78. package/templates/plan.md +78 -0
  79. package/templates/proposal.md +36 -0
  80. package/templates/spec.md +57 -0
  81. package/templates/tasks.md +29 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Hank Liu
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.
package/README.md ADDED
@@ -0,0 +1,328 @@
1
+ # SuperSpec
2
+
3
+ > **Spec-Driven Development Framework**
4
+
5
+ SuperSpec is a unified development framework that combines spec-driven documentation with TDD discipline.
6
+
7
+ ## Core Philosophy
8
+
9
+ ```
10
+ Every Scenario becomes a test. Every test traces back to a Scenario.
11
+ ```
12
+
13
+ | Principle | Description |
14
+ |-----------|-------------|
15
+ | **Specs First** | All development has Specs as the single source of truth |
16
+ | **TDD Enforced** | Write tests first, watch them fail, then implement |
17
+ | **Two-Phase Review** | Spec Compliance Review → Code Quality Review |
18
+ | **Evidence First** | Verification over claims |
19
+ | **Delta Tracking** | Structured change history |
20
+ | **Archive Preservation** | Complete development documentation |
21
+
22
+ ## Unified Workflow
23
+
24
+ ```
25
+ ┌─────────────────────────────────────────────────────────────────────────┐
26
+ │ FULL WORKFLOW (large features, team review) │
27
+ ├─────────────────────────────────────────────────────────────────────────┤
28
+ │ /superspec:brainstorm → Progressive design (Explore → Propose → Spec)│
29
+ │ ↓ │
30
+ │ superspec validate → Validate specs (CLI) + team review │
31
+ │ ↓ │
32
+ │ /superspec:plan → Create TDD implementation plan │
33
+ └─────────────────────────────────────────────────────────────────────────┘
34
+
35
+ ┌─────────────────────────────────────────────────────────────────────────┐
36
+ │ FAST TRACK (small-medium features) │
37
+ ├─────────────────────────────────────────────────────────────────────────┤
38
+ │ /superspec:kickoff → All-in-one: brainstorm + validate + plan │
39
+ └─────────────────────────────────────────────────────────────────────────┘
40
+
41
+ ↓ (both paths continue)
42
+
43
+ /superspec:execute Subagent-driven TDD implementation
44
+
45
+ /superspec:verify Verify implementation matches specs
46
+
47
+ /superspec:finish-branch Complete branch (merge/PR)
48
+
49
+ /superspec:archive Archive changes, apply Delta
50
+ ```
51
+
52
+ ### Four Phases of Brainstorming
53
+
54
+ ```
55
+ +----------------------------------------------------------+
56
+ | /superspec:brainstorm |
57
+ +----------------------------------------------------------+
58
+ | |
59
+ | Phase 1: EXPLORE |
60
+ | - Free exploration, understand the problem |
61
+ | - Ask clarifying questions, visualize ideas |
62
+ | |
63
+ | | Problem is clear |
64
+ | v |
65
+ | |
66
+ | Phase 2: PROPOSE -> proposal.md |
67
+ | - Define Why (problem/opportunity) |
68
+ | - Define What Changes (change list) |
69
+ | - Define Capabilities (new/modified features) |
70
+ | - Define Impact (affected areas) |
71
+ | |
72
+ | | Scope is defined |
73
+ | v |
74
+ | |
75
+ | Phase 3: DESIGN -> design.md |
76
+ | - Compare 2-3 technical approaches |
77
+ | - Select recommended approach with rationale |
78
+ | - Document trade-offs |
79
+ | |
80
+ | | Approach is decided |
81
+ | v |
82
+ | |
83
+ | Phase 4: SPEC -> specs/*.md |
84
+ | - Define Requirements (System SHALL...) |
85
+ | - Define Scenarios (WHEN/THEN -> each becomes a test) |
86
+ | |
87
+ +----------------------------------------------------------+
88
+ ```
89
+
90
+ ## Quick Start
91
+
92
+ ### Installation
93
+
94
+ ```bash
95
+ # Install CLI
96
+ npm install -g superspec
97
+
98
+ # Or install from source
99
+ git clone https://github.com/your-org/superspec
100
+ cd superspec
101
+ npm install
102
+ npm run build
103
+ npm link
104
+ ```
105
+
106
+ ### Claude Code Integration
107
+
108
+ Skills are automatically installed to `~/.claude/skills/` when you run `superspec init`.
109
+
110
+ ### Initialize Project
111
+
112
+ ```bash
113
+ superspec init
114
+ ```
115
+
116
+ This will create:
117
+ ```
118
+ your-project/
119
+ └── superspec/
120
+ ├── project.yaml # Project configuration
121
+ ├── specs/ # Specifications (source of truth)
122
+ └── changes/ # Change proposals
123
+ ```
124
+
125
+ ### Basic Usage
126
+
127
+ ```bash
128
+ # FAST TRACK: Idea to plan in one session
129
+ /superspec:kickoff
130
+
131
+ # OR FULL WORKFLOW: Separate phases with review points
132
+ # 1. Start a new change (four-phase brainstorming)
133
+ /superspec:brainstorm
134
+
135
+ # 2. Validate specifications (CLI command)
136
+ superspec validate add-feature --strict
137
+
138
+ # 3. Create implementation plan
139
+ /superspec:plan
140
+
141
+ # THEN CONTINUE (both paths):
142
+ # 4. Execute plan (subagent-driven TDD)
143
+ /superspec:execute
144
+
145
+ # 5. Verify implementation (CLI command)
146
+ superspec verify add-feature
147
+
148
+ # 6. Complete branch (merge/PR/keep/discard)
149
+ /superspec:finish-branch
150
+
151
+ # 7. Archive changes (CLI command)
152
+ superspec archive add-feature --yes
153
+ ```
154
+
155
+ ## Directory Structure
156
+
157
+ ### Project Structure
158
+
159
+ ```
160
+ your-project/
161
+ ├── superspec/
162
+ │ ├── specs/ # Specifications (source of truth)
163
+ │ │ └── [capability]/
164
+ │ │ └── spec.md
165
+ │ │
166
+ │ └── changes/ # Change proposals
167
+ │ ├── [change-id]/ # In progress
168
+ │ │ ├── proposal.md # Why + What
169
+ │ │ ├── design.md # How (technical approach)
170
+ │ │ ├── specs/ # Delta Specs
171
+ │ │ ├── plan.md # TDD implementation plan
172
+ │ │ └── tasks.md # Task list
173
+ │ │
174
+ │ └── archive/ # Completed
175
+ │ └── YYYY-MM-DD-[id]/
176
+
177
+ └── src/ # Actual code
178
+ ```
179
+
180
+ ### Spec Format
181
+
182
+ ```markdown
183
+ # [Capability] Specification
184
+
185
+ ## Purpose
186
+ [Feature purpose]
187
+
188
+ ## Requirements
189
+
190
+ ### Requirement: [Requirement Name]
191
+ The system SHALL [behavior description]
192
+
193
+ #### Scenario: [Scenario Name]
194
+ - **WHEN** [trigger condition]
195
+ - **THEN** [expected result]
196
+ ```
197
+
198
+ ### Spec-Test Mapping
199
+
200
+ ```
201
+ Spec Scenario TDD Test
202
+ ===============================================================
203
+ #### Scenario: Valid login -> test('Valid login', () => {
204
+ - WHEN valid credentials const result = login(valid);
205
+ - THEN grants access expect(result.granted).toBe(true);
206
+ });
207
+ ===============================================================
208
+ ```
209
+
210
+ ## CLI Commands
211
+
212
+ ```bash
213
+ # Initialize
214
+ superspec init [path] # Initialize project
215
+
216
+ # Query
217
+ superspec list # List changes
218
+ superspec list --specs # List specifications
219
+ superspec show [item] # Show details
220
+
221
+ # Validate
222
+ superspec validate [id] # Validate specification
223
+ superspec validate [id] --strict # Strict mode
224
+
225
+ # Verify
226
+ superspec verify [id] # Verify implementation matches specs
227
+ superspec verify [id] --strict # Fail on extra code/tests
228
+ superspec verify [id] --verbose # Show detailed matching
229
+
230
+ # Archive
231
+ superspec archive [id] # Archive change
232
+ superspec archive [id] --yes # Non-interactive mode
233
+ ```
234
+
235
+ ## Slash Commands (AI Assistant)
236
+
237
+ | Command | Description |
238
+ |---------|-------------|
239
+ | `/superspec:kickoff` | **Fast track**: brainstorm + validate + plan in one session |
240
+ | `/superspec:brainstorm` | **Full workflow**: Progressive design (Explore → Propose → Spec) |
241
+ | `/superspec:plan` | Create TDD implementation plan (after brainstorm) |
242
+ | `/superspec:execute` | Subagent-driven execution |
243
+ | `/superspec:verify` | Verify implementation matches specs |
244
+ | `/superspec:finish-branch` | Complete branch (merge/PR/keep/discard) |
245
+ | `/superspec:archive` | Archive changes |
246
+
247
+ ## Skill System
248
+
249
+ ### Design Phase
250
+ - `superspec:kickoff` - **Fast track**: idea to plan in one session
251
+ - `superspec:brainstorm` - **Full workflow**: progressive design only
252
+
253
+ ### Planning Phase
254
+ - `superspec:plan` - Create TDD plan (after brainstorm)
255
+ - `git-worktree` - Git Worktree management
256
+
257
+ ### Implementation Phase
258
+ - `tdd` - TDD cycle (with `testing-anti-patterns.md` reference)
259
+ - `superspec:execute` - Subagent-driven development (default)
260
+ - `executing-plans` - Batch execution with checkpoints (alternative)
261
+ - `dispatching-parallel-agents` - For 2+ independent parallel tasks
262
+ - `systematic-debugging` - Systematic debugging (with support files)
263
+
264
+ ### Quality & Discipline Phase
265
+ - `verification-before-completion` - Evidence before completion claims
266
+ - `receiving-code-review` - Handle code review feedback professionally
267
+
268
+ ### Review Phase
269
+ - `spec-validation` - Specification validation
270
+ - `code-review` - Code review (with `code-reviewer.md` template)
271
+
272
+ ### Completion Phase
273
+ - `superspec:verify` - Verify implementation
274
+ - `superspec:finish-branch` - Complete branch (merge/PR/keep/discard)
275
+ - `superspec:archive` - Archive changes
276
+
277
+ ## Two-Phase Review
278
+
279
+ ```
280
+ +------------------------------------------+
281
+ | Stage 1: Spec Compliance Review |
282
+ | - Read Spec files |
283
+ | - Check each Requirement is implemented |
284
+ | - Check each Scenario has a test |
285
+ | - Check no extra/missing features |
286
+ +------------------------------------------+
287
+ |
288
+ [After passing]
289
+ v
290
+ +------------------------------------------+
291
+ | Stage 2: Code Quality Review |
292
+ | - Error handling |
293
+ | - Type safety |
294
+ | - SOLID principles |
295
+ | - Test quality |
296
+ +------------------------------------------+
297
+ ```
298
+
299
+ ## Iron Rules
300
+
301
+ ### TDD Rule
302
+ ```
303
+ No production code without a failing test first
304
+ ```
305
+
306
+ ### Spec Rule
307
+ ```
308
+ Specs are the source of truth. Changes are proposals.
309
+ ```
310
+
311
+ ### SuperSpec Rule
312
+ ```
313
+ Every Scenario becomes a test. Every test traces back to a Scenario.
314
+ ```
315
+
316
+ ### Verification Rule
317
+ ```
318
+ No completion claims without fresh verification evidence
319
+ ```
320
+ "It should work" is not evidence. Run the test. Show the output. Then claim completion.
321
+
322
+ ## License
323
+
324
+ MIT License
325
+
326
+ ---
327
+
328
+ **SuperSpec** - Spec-Driven + TDD Discipline = Traceable High-Quality Development
@@ -0,0 +1,7 @@
1
+ interface ArchiveOptions {
2
+ yes?: boolean;
3
+ skipSpecs?: boolean;
4
+ }
5
+ export declare function archiveCommand(id: string | undefined, options: ArchiveOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/archive.ts"],"names":[],"mappings":"AAgBA,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAWD,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuHnG"}
@@ -0,0 +1,322 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync, rmSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { PALETTE, ICONS, commandHeader, sectionHeader, startSpinner, spinnerSuccess, spinnerFail, isInteractive, selectChange, confirmDangerous, displayCommand, } from '../ui/index.js';
4
+ export async function archiveCommand(id, options) {
5
+ const superspecPath = join(process.cwd(), 'superspec');
6
+ if (!existsSync(superspecPath)) {
7
+ console.log(` ${ICONS.error} ${PALETTE.error('SuperSpec not initialized.')}`);
8
+ console.log(` ${PALETTE.dim('Run:')} ${PALETTE.accent('superspec init')}`);
9
+ process.exit(1);
10
+ }
11
+ // Interactive mode if no ID provided
12
+ if (!id) {
13
+ if (!isInteractive()) {
14
+ console.log(PALETTE.error('Please specify a change ID to archive.'));
15
+ process.exit(1);
16
+ }
17
+ const changesPath = join(superspecPath, 'changes');
18
+ const changes = [];
19
+ if (existsSync(changesPath)) {
20
+ const entries = readdirSync(changesPath, { withFileTypes: true });
21
+ for (const entry of entries) {
22
+ if (entry.isDirectory() && entry.name !== 'archive') {
23
+ changes.push(entry.name);
24
+ }
25
+ }
26
+ }
27
+ if (changes.length === 0) {
28
+ console.log(` ${ICONS.warning} ${PALETTE.warning('No active changes found.')}`);
29
+ return;
30
+ }
31
+ id = await selectChange(changes, 'Select a change to archive:');
32
+ }
33
+ const changePath = join(superspecPath, 'changes', id);
34
+ if (!existsSync(changePath)) {
35
+ console.log(` ${ICONS.error} ${PALETTE.error(`Change not found: ${id}`)}`);
36
+ process.exit(1);
37
+ }
38
+ console.log(commandHeader(`archive ${id}`, 'Archive completed change'));
39
+ // Check prerequisites
40
+ const checkSpinner = startSpinner('Checking prerequisites...');
41
+ const checks = checkPrerequisites(changePath);
42
+ if (!checks.valid) {
43
+ spinnerFail(checkSpinner, 'Prerequisites not met');
44
+ console.log(sectionHeader('Issues Found', '❌'));
45
+ console.log();
46
+ for (const issue of checks.issues) {
47
+ console.log(` ${ICONS.error} ${PALETTE.muted(issue)}`);
48
+ }
49
+ console.log();
50
+ process.exit(1);
51
+ }
52
+ spinnerSuccess(checkSpinner, 'All prerequisites met');
53
+ // Show what will happen
54
+ console.log(sectionHeader('Archive Actions', '📦'));
55
+ console.log();
56
+ const actions = [];
57
+ if (!options.skipSpecs) {
58
+ actions.push('Apply delta specs to main specifications');
59
+ }
60
+ actions.push(`Move ${id} to archive/`);
61
+ for (let i = 0; i < actions.length; i++) {
62
+ console.log(` ${PALETTE.accent(`${i + 1}.`)} ${actions[i]}`);
63
+ }
64
+ console.log();
65
+ // Confirm
66
+ if (!options.yes) {
67
+ console.log(` ${ICONS.warning} ${PALETTE.warning('This action will modify your main specifications.')}`);
68
+ console.log();
69
+ const confirmed = await confirmDangerous({
70
+ message: 'Proceed with archive?',
71
+ });
72
+ if (!confirmed) {
73
+ console.log();
74
+ console.log(` ${PALETTE.dim('Archive cancelled.')}`);
75
+ return;
76
+ }
77
+ }
78
+ console.log();
79
+ try {
80
+ // Apply deltas
81
+ if (!options.skipSpecs) {
82
+ await applyDeltas(superspecPath, changePath);
83
+ }
84
+ // Move to archive
85
+ await moveToArchive(superspecPath, changePath, id);
86
+ console.log();
87
+ console.log(` ${PALETTE.subtle('─'.repeat(50))}`);
88
+ console.log();
89
+ console.log(` ${ICONS.success} ${PALETTE.bold(PALETTE.success('Archive complete!'))}`);
90
+ console.log(sectionHeader('Next Steps', '🚀'));
91
+ console.log();
92
+ displayCommand('superspec validate --all', 'Run validation');
93
+ console.log(` ${PALETTE.dim('Verify main specs are updated correctly')}`);
94
+ console.log();
95
+ }
96
+ catch (err) {
97
+ console.log();
98
+ console.log(` ${ICONS.error} ${PALETTE.bold(PALETTE.error('Archive failed'))}`);
99
+ console.log(` ${PALETTE.muted(err instanceof Error ? err.message : String(err))}`);
100
+ process.exit(1);
101
+ }
102
+ }
103
+ function checkPrerequisites(changePath) {
104
+ const issues = [];
105
+ // Check required files
106
+ if (!existsSync(join(changePath, 'proposal.md'))) {
107
+ issues.push('Missing proposal.md');
108
+ }
109
+ if (!existsSync(join(changePath, 'design.md'))) {
110
+ issues.push('Missing design.md');
111
+ }
112
+ if (!existsSync(join(changePath, 'specs'))) {
113
+ issues.push('Missing specs/ directory');
114
+ }
115
+ // Check tasks completion (if tasks.md exists)
116
+ const tasksPath = join(changePath, 'tasks.md');
117
+ if (existsSync(tasksPath)) {
118
+ const content = readFileSync(tasksPath, 'utf-8');
119
+ const incomplete = (content.match(/^- \[ \]/gm) ?? []).length;
120
+ if (incomplete > 0) {
121
+ issues.push(`${incomplete} incomplete tasks in tasks.md`);
122
+ }
123
+ }
124
+ return { valid: issues.length === 0, issues };
125
+ }
126
+ async function applyDeltas(superspecPath, changePath) {
127
+ const specsPath = join(changePath, 'specs');
128
+ const mainSpecsPath = join(superspecPath, 'specs');
129
+ if (!existsSync(specsPath)) {
130
+ return;
131
+ }
132
+ const spinner = startSpinner('Applying delta specs...');
133
+ const entries = readdirSync(specsPath, { withFileTypes: true });
134
+ let appliedCount = 0;
135
+ for (const entry of entries) {
136
+ if (entry.isDirectory()) {
137
+ const deltaSpecPath = join(specsPath, entry.name, 'spec.md');
138
+ if (existsSync(deltaSpecPath)) {
139
+ const content = readFileSync(deltaSpecPath, 'utf-8');
140
+ const operations = parseDeltaOperations(content);
141
+ if (operations.length > 0) {
142
+ await applyDeltaToMainSpec(mainSpecsPath, entry.name, operations, content);
143
+ appliedCount++;
144
+ }
145
+ else {
146
+ // No delta markers - this is a new spec, copy as-is
147
+ const targetDir = join(mainSpecsPath, entry.name);
148
+ const targetPath = join(targetDir, 'spec.md');
149
+ if (!existsSync(targetDir)) {
150
+ mkdirSync(targetDir, { recursive: true });
151
+ }
152
+ copyFileSync(deltaSpecPath, targetPath);
153
+ appliedCount++;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ spinnerSuccess(spinner, `Applied ${appliedCount} specifications`);
159
+ }
160
+ function parseDeltaOperations(content) {
161
+ const operations = [];
162
+ const lines = content.split('\n');
163
+ let currentSection = null;
164
+ let currentReq = '';
165
+ let currentContent = [];
166
+ for (const line of lines) {
167
+ // Detect section headers
168
+ if (line.match(/^## ADDED Requirements/i)) {
169
+ currentSection = 'added';
170
+ continue;
171
+ }
172
+ if (line.match(/^## MODIFIED Requirements/i)) {
173
+ currentSection = 'modified';
174
+ continue;
175
+ }
176
+ if (line.match(/^## REMOVED Requirements/i)) {
177
+ currentSection = 'removed';
178
+ continue;
179
+ }
180
+ if (line.match(/^## RENAMED Requirements/i)) {
181
+ currentSection = 'renamed';
182
+ continue;
183
+ }
184
+ // Detect new requirement
185
+ const reqMatch = line.match(/^### Requirement: (.+)$/);
186
+ if (reqMatch?.[1] && currentSection) {
187
+ // Save previous if exists
188
+ if (currentReq) {
189
+ operations.push({
190
+ type: currentSection,
191
+ requirement: currentReq,
192
+ content: currentContent.join('\n'),
193
+ });
194
+ }
195
+ currentReq = reqMatch[1];
196
+ currentContent = [line];
197
+ continue;
198
+ }
199
+ // Detect renamed format
200
+ if (currentSection === 'renamed') {
201
+ const fromMatch = line.match(/^- FROM:.*`### Requirement: (.+)`/);
202
+ const toMatch = line.match(/^- TO:.*`### Requirement: (.+)`/);
203
+ if (fromMatch?.[1]) {
204
+ currentReq = fromMatch[1];
205
+ }
206
+ if (toMatch?.[1] && currentReq) {
207
+ operations.push({
208
+ type: 'renamed',
209
+ requirement: currentReq,
210
+ fromName: currentReq,
211
+ toName: toMatch[1],
212
+ });
213
+ currentReq = '';
214
+ }
215
+ continue;
216
+ }
217
+ // Collect content
218
+ if (currentReq && currentSection) {
219
+ currentContent.push(line);
220
+ }
221
+ }
222
+ // Don't forget last one
223
+ if (currentReq && currentSection) {
224
+ operations.push({
225
+ type: currentSection,
226
+ requirement: currentReq,
227
+ content: currentContent.join('\n'),
228
+ });
229
+ }
230
+ return operations;
231
+ }
232
+ async function applyDeltaToMainSpec(mainSpecsPath, capability, operations, fullContent) {
233
+ const mainSpecDir = join(mainSpecsPath, capability);
234
+ const mainSpecPath = join(mainSpecDir, 'spec.md');
235
+ // Create directory if needed
236
+ if (!existsSync(mainSpecDir)) {
237
+ mkdirSync(mainSpecDir, { recursive: true });
238
+ }
239
+ let mainContent = '';
240
+ if (existsSync(mainSpecPath)) {
241
+ mainContent = readFileSync(mainSpecPath, 'utf-8');
242
+ }
243
+ // Sort operations: RENAMED → REMOVED → MODIFIED → ADDED
244
+ const sorted = [...operations].sort((a, b) => {
245
+ const order = { renamed: 0, removed: 1, modified: 2, added: 3 };
246
+ return order[a.type] - order[b.type];
247
+ });
248
+ for (const op of sorted) {
249
+ switch (op.type) {
250
+ case 'renamed':
251
+ if (op.fromName && op.toName) {
252
+ mainContent = mainContent.replace(`### Requirement: ${op.fromName}`, `### Requirement: ${op.toName}`);
253
+ console.log(` ${PALETTE.cyan('→')} Renamed: ${op.fromName} → ${op.toName}`);
254
+ }
255
+ break;
256
+ case 'removed':
257
+ // Remove the requirement section
258
+ const removePattern = new RegExp(`### Requirement: ${escapeRegex(op.requirement)}[\\s\\S]*?(?=### Requirement:|$)`, 'g');
259
+ mainContent = mainContent.replace(removePattern, '');
260
+ console.log(` ${PALETTE.error('−')} Removed: ${op.requirement}`);
261
+ break;
262
+ case 'modified':
263
+ // Replace existing requirement
264
+ if (op.content) {
265
+ const modifyPattern = new RegExp(`### Requirement: ${escapeRegex(op.requirement)}[\\s\\S]*?(?=### Requirement:|$)`, 'g');
266
+ mainContent = mainContent.replace(modifyPattern, op.content.trim() + '\n\n');
267
+ console.log(` ${PALETTE.warning('~')} Modified: ${op.requirement}`);
268
+ }
269
+ break;
270
+ case 'added':
271
+ // Append to end of requirements section
272
+ if (op.content) {
273
+ mainContent = mainContent.trimEnd() + '\n\n' + op.content.trim() + '\n';
274
+ console.log(` ${PALETTE.success('+')} Added: ${op.requirement}`);
275
+ }
276
+ break;
277
+ }
278
+ }
279
+ // Write updated spec
280
+ writeFileSync(mainSpecPath, mainContent.trim() + '\n');
281
+ }
282
+ function escapeRegex(str) {
283
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
284
+ }
285
+ async function moveToArchive(superspecPath, changePath, id) {
286
+ const date = new Date().toISOString().split('T')[0];
287
+ const archiveId = `${date}-${id}`;
288
+ const archivePath = join(superspecPath, 'changes', 'archive', archiveId);
289
+ const spinner = startSpinner(`Moving to archive/${archiveId}...`);
290
+ // Create archive directory
291
+ mkdirSync(archivePath, { recursive: true });
292
+ // Move all files
293
+ const entries = readdirSync(changePath, { withFileTypes: true });
294
+ for (const entry of entries) {
295
+ const srcPath = join(changePath, entry.name);
296
+ const destPath = join(archivePath, entry.name);
297
+ if (entry.isDirectory()) {
298
+ copyDirSync(srcPath, destPath);
299
+ }
300
+ else {
301
+ copyFileSync(srcPath, destPath);
302
+ }
303
+ }
304
+ // Remove original
305
+ rmSync(changePath, { recursive: true });
306
+ spinnerSuccess(spinner, `Archived to ${archiveId}`);
307
+ }
308
+ function copyDirSync(src, dest) {
309
+ mkdirSync(dest, { recursive: true });
310
+ const entries = readdirSync(src, { withFileTypes: true });
311
+ for (const entry of entries) {
312
+ const srcPath = join(src, entry.name);
313
+ const destPath = join(dest, entry.name);
314
+ if (entry.isDirectory()) {
315
+ copyDirSync(srcPath, destPath);
316
+ }
317
+ else {
318
+ copyFileSync(srcPath, destPath);
319
+ }
320
+ }
321
+ }
322
+ //# sourceMappingURL=archive.js.map