@specsafe/cli 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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +310 -0
  3. package/dist/commands/archive.d.ts +3 -0
  4. package/dist/commands/archive.d.ts.map +1 -0
  5. package/dist/commands/archive.js +99 -0
  6. package/dist/commands/archive.js.map +1 -0
  7. package/dist/commands/code.d.ts +3 -0
  8. package/dist/commands/code.d.ts.map +1 -0
  9. package/dist/commands/code.js +53 -0
  10. package/dist/commands/code.js.map +1 -0
  11. package/dist/commands/complete.d.ts +3 -0
  12. package/dist/commands/complete.d.ts.map +1 -0
  13. package/dist/commands/complete.js +137 -0
  14. package/dist/commands/complete.js.map +1 -0
  15. package/dist/commands/doctor.d.ts +3 -0
  16. package/dist/commands/doctor.d.ts.map +1 -0
  17. package/dist/commands/doctor.js +204 -0
  18. package/dist/commands/doctor.js.map +1 -0
  19. package/dist/commands/init.d.ts +3 -0
  20. package/dist/commands/init.d.ts.map +1 -0
  21. package/dist/commands/init.js +80 -0
  22. package/dist/commands/init.js.map +1 -0
  23. package/dist/commands/list.d.ts +3 -0
  24. package/dist/commands/list.d.ts.map +1 -0
  25. package/dist/commands/list.js +122 -0
  26. package/dist/commands/list.js.map +1 -0
  27. package/dist/commands/new.d.ts +3 -0
  28. package/dist/commands/new.d.ts.map +1 -0
  29. package/dist/commands/new.js +141 -0
  30. package/dist/commands/new.js.map +1 -0
  31. package/dist/commands/qa.d.ts +3 -0
  32. package/dist/commands/qa.d.ts.map +1 -0
  33. package/dist/commands/qa.js +143 -0
  34. package/dist/commands/qa.js.map +1 -0
  35. package/dist/commands/spec.d.ts +3 -0
  36. package/dist/commands/spec.d.ts.map +1 -0
  37. package/dist/commands/spec.js +70 -0
  38. package/dist/commands/spec.js.map +1 -0
  39. package/dist/commands/status.d.ts +3 -0
  40. package/dist/commands/status.d.ts.map +1 -0
  41. package/dist/commands/status.js +47 -0
  42. package/dist/commands/status.js.map +1 -0
  43. package/dist/commands/test.d.ts +3 -0
  44. package/dist/commands/test.d.ts.map +1 -0
  45. package/dist/commands/test.js +134 -0
  46. package/dist/commands/test.js.map +1 -0
  47. package/dist/config.d.ts +17 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/config.js +44 -0
  50. package/dist/config.js.map +1 -0
  51. package/dist/index.d.ts +7 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +49 -0
  54. package/dist/index.js.map +1 -0
  55. package/package.json +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agentic Engineering
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,310 @@
1
+ # @specsafe/cli
2
+
3
+ <p align="center">
4
+ <img src="https://img.shields.io/npm/v/@specsafe/cli.svg" alt="npm version">
5
+ <img src="https://img.shields.io/npm/l/@specsafe/cli.svg" alt="license">
6
+ </p>
7
+
8
+ **SpecSafe** is a spec-driven development framework for AI-assisted engineering. It provides a structured workflow for turning specifications into tested, production-ready code with full traceability from requirements to implementation.
9
+
10
+ ## Installation
11
+
12
+ ### Global Installation
13
+ ```bash
14
+ npm install -g @specsafe/cli
15
+ ```
16
+
17
+ ### Using npx (no installation)
18
+ ```bash
19
+ npx @specsafe/cli <command>
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ The SpecSafe workflow follows a 7-step cycle:
25
+
26
+ ```bash
27
+ # 1. Initialize a new SpecSafe project
28
+ specsafe init my-project
29
+
30
+ # 2. Create a new spec
31
+ specsafe new login-feature
32
+
33
+ # 3. Write the specification (creates specs/active/login-feature.spec.md)
34
+ # ... edit the spec file with your requirements
35
+
36
+ # 4. Generate tests from the spec
37
+ specsafe spec specs/active/login-feature.spec.md
38
+
39
+ # 5. Move to implementation phase
40
+ specsafe test login-feature
41
+
42
+ # 6. Write your code, then mark as ready for QA
43
+ specsafe code login-feature
44
+
45
+ # 7. Mark as complete and archive
46
+ specsafe qa login-feature # Or skip with --force
47
+ specsafe complete login-feature
48
+ specsafe archive login-feature
49
+ ```
50
+
51
+ ## Commands
52
+
53
+ ### `init [directory]`
54
+ Initialize a new SpecSafe project.
55
+
56
+ ```bash
57
+ specsafe init my-project
58
+ cd my-project
59
+ ```
60
+
61
+ Creates the following structure:
62
+ ```
63
+ my-project/
64
+ ├── specs/
65
+ │ ├── active/ # Active specifications
66
+ │ ├── completed/ # Completed specs
67
+ │ └── archived/ # Archived specs
68
+ ├── src/
69
+ │ ├── specs/ # Generated test files
70
+ │ └── impl/ # Implementation files
71
+ ├── specsafe.config.json
72
+ └── package.json
73
+ ```
74
+
75
+ **Options:**
76
+ - `-f, --force` - Overwrite existing directory
77
+
78
+ ---
79
+
80
+ ### `new <spec-id>`
81
+ Create a new specification file.
82
+
83
+ ```bash
84
+ specsafe new user-authentication
85
+ ```
86
+
87
+ Creates `specs/active/user-authentication.spec.md` with a template structure including:
88
+ - Title and description
89
+ - Requirements section
90
+ - Acceptance criteria
91
+ - Technical notes
92
+
93
+ ---
94
+
95
+ ### `spec <spec-file>`
96
+ Parse a specification and generate tests.
97
+
98
+ ```bash
99
+ # Parse a spec and generate tests
100
+ specsafe spec specs/active/login-feature.spec.md
101
+
102
+ # Output to a specific directory
103
+ specsafe spec specs/active/login-feature.spec.md --output ./tests
104
+
105
+ # Use Jest instead of Vitest
106
+ specsafe spec specs/active/login-feature.spec.md --framework jest
107
+ ```
108
+
109
+ **Options:**
110
+ - `-o, --output <dir>` - Output directory for generated tests
111
+ - `-f, --framework <framework>` - Test framework (vitest|jest, default: vitest)
112
+ - `-w, --watch` - Watch mode for continuous regeneration
113
+
114
+ ---
115
+
116
+ ### `test <spec-id>`
117
+ Move a specification to the "test" phase.
118
+
119
+ ```bash
120
+ specsafe test login-feature
121
+ ```
122
+
123
+ This updates the spec status and prepares it for implementation.
124
+
125
+ ---
126
+
127
+ ### `code <spec-id>`
128
+ Move a specification to the "code" phase.
129
+
130
+ ```bash
131
+ specsafe code login-feature
132
+ ```
133
+
134
+ Indicates that implementation is in progress.
135
+
136
+ ---
137
+
138
+ ### `qa <spec-id>`
139
+ Move a specification to QA or mark QA as complete.
140
+
141
+ ```bash
142
+ # Normal QA flow
143
+ specsafe qa login-feature
144
+
145
+ # Skip QA (with flag)
146
+ specsafe qa login-feature --force
147
+ ```
148
+
149
+ **Options:**
150
+ - `-f, --force` - Skip QA phase
151
+
152
+ ---
153
+
154
+ ### `complete <spec-id>`
155
+ Mark a specification as complete.
156
+
157
+ ```bash
158
+ specsafe complete login-feature
159
+ ```
160
+
161
+ Moves the spec from active to completed status.
162
+
163
+ ---
164
+
165
+ ### `archive <spec-id>`
166
+ Archive a completed specification.
167
+
168
+ ```bash
169
+ specsafe archive login-feature
170
+ ```
171
+
172
+ Moves the spec from completed to archived status.
173
+
174
+ ---
175
+
176
+ ### `status [spec-id]`
177
+ Show the status of specs.
178
+
179
+ ```bash
180
+ # Show all specs and their status
181
+ specsafe status
182
+
183
+ # Show status of a specific spec
184
+ specsafe status login-feature
185
+ ```
186
+
187
+ ---
188
+
189
+ ### `list [phase]`
190
+ List specifications by phase.
191
+
192
+ ```bash
193
+ # List all specs
194
+ specsafe list
195
+
196
+ # List specs in specific phase
197
+ specsafe list active
198
+ specsafe list completed
199
+ specsafe list archived
200
+ specsafe list test
201
+ specsafe list code
202
+ specsafe list qa
203
+ ```
204
+
205
+ ---
206
+
207
+ ### `validate <spec-id>`
208
+ Validate a specification file for correctness.
209
+
210
+ ```bash
211
+ specsafe validate login-feature
212
+ ```
213
+
214
+ Checks:
215
+ - Required sections present
216
+ - Valid requirement IDs
217
+ - Proper markdown structure
218
+ - Schema compliance
219
+
220
+ ---
221
+
222
+ ## Configuration
223
+
224
+ Create a `specsafe.config.json` file in your project root:
225
+
226
+ ```json
227
+ {
228
+ "projectName": "My Project",
229
+ "specsDir": "./specs",
230
+ "srcDir": "./src",
231
+ "testDir": "./src/specs",
232
+ "implDir": "./src/impl",
233
+ "defaultFramework": "vitest",
234
+ "templates": {
235
+ "spec": "./templates/custom-spec.md"
236
+ },
237
+ "phases": {
238
+ "autoArchive": true,
239
+ "requireQA": false
240
+ }
241
+ }
242
+ ```
243
+
244
+ ### Configuration Options
245
+
246
+ | Option | Type | Default | Description |
247
+ |--------|------|---------|-------------|
248
+ | `projectName` | string | `"SpecSafe Project"` | Project name for generated files |
249
+ | `specsDir` | string | `"./specs"` | Directory for specification files |
250
+ | `srcDir` | string | `"./src"` | Source code directory |
251
+ | `testDir` | string | `"./src/specs"` | Test file output directory |
252
+ | `implDir` | string | `"./src/impl"` | Implementation directory |
253
+ | `defaultFramework` | string | `"vitest"` | Default test framework |
254
+ | `templates.spec` | string | - | Path to custom spec template |
255
+ | `phases.autoArchive` | boolean | `false` | Auto-archive on complete |
256
+ | `phases.requireQA` | boolean | `true` | Require QA phase before complete |
257
+
258
+ ## Project Structure
259
+
260
+ A typical SpecSafe project:
261
+
262
+ ```
263
+ my-project/
264
+ ├── specs/
265
+ │ ├── active/
266
+ │ │ └── login-feature.spec.md
267
+ │ ├── completed/
268
+ │ └── archived/
269
+ ├── src/
270
+ │ ├── specs/
271
+ │ │ └── login-feature.spec.ts
272
+ │ └── impl/
273
+ │ └── login-feature.ts
274
+ ├── specsafe.config.json
275
+ └── package.json
276
+ ```
277
+
278
+ ## Related Packages
279
+
280
+ - [@specsafe/core](https://www.npmjs.com/package/@specsafe/core) - Core workflow engine and types
281
+ - [@specsafe/test-gen](https://www.npmjs.com/package/@specsafe/test-gen) - Test generation library
282
+
283
+ ## Specification Format
284
+
285
+ Specifications are markdown files with a specific structure:
286
+
287
+ ```markdown
288
+ # Feature Title
289
+
290
+ ## Description
291
+ Brief description of the feature.
292
+
293
+ ## Requirements
294
+
295
+ ### REQ-001: Requirement Title
296
+ **Given** initial context
297
+ **When** action is performed
298
+ **Then** expected result
299
+
300
+ ## Acceptance Criteria
301
+ - [ ] Criterion 1
302
+ - [ ] Criterion 2
303
+
304
+ ## Technical Notes
305
+ Implementation hints and notes.
306
+ ```
307
+
308
+ ## License
309
+
310
+ MIT © Agentic Engineering
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const archiveCommand: Command;
3
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,cAAc,SAoGvB,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { Workflow, ProjectTracker, validateSpecId } from '@specsafe/core';
5
+ import { access, rename, mkdir } from 'fs/promises';
6
+ import { join } from 'path';
7
+ export const archiveCommand = new Command('archive')
8
+ .description('Move a COMPLETE spec to ARCHIVED stage')
9
+ .argument('<id>', 'Spec ID to archive')
10
+ .option('-n, --dry-run', 'Preview changes without writing files')
11
+ .action(async (id, options) => {
12
+ const spinner = ora(`Archiving ${id}...`).start();
13
+ try {
14
+ // Validate spec ID format
15
+ validateSpecId(id);
16
+ const workflow = new Workflow();
17
+ const tracker = new ProjectTracker(process.cwd());
18
+ // Load existing specs from disk
19
+ await tracker.loadSpecsIntoWorkflow(workflow);
20
+ // Check if spec exists
21
+ const spec = workflow.getSpec(id);
22
+ if (!spec) {
23
+ spinner.fail(chalk.red(`Spec '${id}' not found in project state.`));
24
+ process.exit(1);
25
+ }
26
+ // Validate spec is in COMPLETE stage
27
+ if (spec.stage !== 'complete') {
28
+ spinner.fail(chalk.red(`Cannot archive spec in ${spec.stage.toUpperCase()} stage. Must be COMPLETE.`));
29
+ console.log(chalk.gray(`Current stage: ${spec.stage.toUpperCase()}`));
30
+ console.log(chalk.gray('Use specsafe complete <id> to move to COMPLETE stage first.'));
31
+ process.exit(1);
32
+ }
33
+ // Move spec file from specs/completed/ to specs/archive/
34
+ const completedDir = join(process.cwd(), 'specs', 'completed');
35
+ const archiveDir = join(process.cwd(), 'specs', 'archive');
36
+ const sourcePath = join(completedDir, `${id}.md`);
37
+ const destPath = join(archiveDir, `${id}.md`);
38
+ // Check if source file exists
39
+ let fileExists = false;
40
+ try {
41
+ await access(sourcePath);
42
+ fileExists = true;
43
+ }
44
+ catch {
45
+ // Source file doesn't exist
46
+ }
47
+ // Handle dry-run mode
48
+ if (options.dryRun) {
49
+ spinner.stop();
50
+ console.log(chalk.cyan('[DRY RUN] Would perform the following actions:\n'));
51
+ console.log(chalk.cyan(' State transition:'));
52
+ console.log(chalk.gray(` COMPLETE → ARCHIVED`));
53
+ if (fileExists) {
54
+ console.log(chalk.cyan(`\n File move:`));
55
+ console.log(chalk.gray(` ${sourcePath} → ${destPath}`));
56
+ }
57
+ else {
58
+ console.log(chalk.cyan(`\n File move:`));
59
+ console.log(chalk.yellow(` Source file not found at ${sourcePath}`));
60
+ console.log(chalk.gray(` Would continue with workflow archive only`));
61
+ }
62
+ console.log(chalk.cyan(`\n Would update PROJECT_STATE.md for spec: ${id}`));
63
+ process.exit(0);
64
+ }
65
+ // Ensure archive directory exists
66
+ await mkdir(archiveDir, { recursive: true });
67
+ // Move the file (if it exists)
68
+ let fileMoved = false;
69
+ try {
70
+ await rename(sourcePath, destPath);
71
+ fileMoved = true;
72
+ }
73
+ catch (err) {
74
+ if (err.code !== 'ENOENT') {
75
+ throw err;
76
+ }
77
+ }
78
+ // Use workflow to archive the spec
79
+ workflow.archiveSpec(id);
80
+ // Update PROJECT_STATE.md
81
+ const archivedSpec = workflow.getSpec(id);
82
+ if (archivedSpec) {
83
+ await tracker.addSpec(archivedSpec);
84
+ }
85
+ spinner.succeed(chalk.green(`Archived ${id}`));
86
+ console.log(chalk.blue(` Stage: ${spec.stage.toUpperCase()} → ARCHIVED`));
87
+ if (fileMoved) {
88
+ console.log(chalk.gray(` File moved to: specs/archive/${id}.md`));
89
+ }
90
+ else {
91
+ console.log(chalk.gray(` No spec file was moved (file not found).`));
92
+ }
93
+ }
94
+ catch (error) {
95
+ spinner.fail(chalk.red(error.message));
96
+ process.exit(1);
97
+ }
98
+ });
99
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;KACjD,WAAW,CAAC,wCAAwC,CAAC;KACrD,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC;KACtC,MAAM,CAAC,eAAe,EAAE,uCAAuC,CAAC;KAChE,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,OAA6B,EAAE,EAAE;IAC1D,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IAElD,IAAI,CAAC;QACH,0BAA0B;QAC1B,cAAc,CAAC,EAAE,CAAC,CAAC;QAEnB,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAElD,gCAAgC;QAChC,MAAM,OAAO,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAE9C,uBAAuB;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,+BAA+B,CAAC,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,qCAAqC;QACrC,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,2BAA2B,CAAC,CAAC,CAAC;YACvG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC,CAAC;YACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,yDAAyD;QACzD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAE9C,8BAA8B;QAC9B,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACzB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;QAED,sBAAsB;QACtB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC,CAAC;YAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;YACnD,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,MAAM,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC,CAAC;gBACxE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,+CAA+C,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,kCAAkC;QAClC,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE7C,+BAA+B;QAC/B,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACnC,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC1B,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QAED,mCAAmC;QACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAEzB,0BAA0B;QAC1B,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC;QAC3E,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC,CAAC;QACxE,CAAC;IAEH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const codeCommand: Command;
3
+ //# sourceMappingURL=code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../src/commands/code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,WAAW,SAiDpB,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { Workflow, ProjectTracker, validateSpecId } from '@specsafe/core';
5
+ export const codeCommand = new Command('code')
6
+ .description('Start implementation (TEST → CODE)')
7
+ .argument('<id>', 'Spec ID')
8
+ .action(async (id) => {
9
+ const spinner = ora(`Starting implementation for ${id}...`).start();
10
+ try {
11
+ // Validate spec ID format
12
+ validateSpecId(id);
13
+ const workflow = new Workflow();
14
+ const tracker = new ProjectTracker(process.cwd());
15
+ // Load existing specs from disk
16
+ await tracker.loadSpecsIntoWorkflow(workflow);
17
+ // Move to code stage (validates tests exist)
18
+ try {
19
+ workflow.moveToCode(id);
20
+ }
21
+ catch (moveError) {
22
+ if (moveError.message.includes('not found')) {
23
+ throw new Error(`Spec '${id}' not found. Run 'specsafe spec ${id}' to create it first.`);
24
+ }
25
+ if (moveError.message.includes('Must be in TEST stage')) {
26
+ throw new Error(`Spec '${id}' is not in TEST stage. Run 'specsafe test ${id}' first.`);
27
+ }
28
+ if (moveError.message.includes('No test files generated')) {
29
+ throw new Error(`Spec '${id}' has no test files. Run 'specsafe test ${id}' to generate tests first.`);
30
+ }
31
+ throw moveError;
32
+ }
33
+ // Persist updated state
34
+ await tracker.addSpec(workflow.getSpec(id));
35
+ spinner.succeed(chalk.green(`Moved ${id} to CODE stage`));
36
+ console.log(chalk.blue('Implement the functionality to pass all tests'));
37
+ console.log(chalk.blue('Then: Run specsafe qa <id> when tests pass'));
38
+ }
39
+ catch (error) {
40
+ spinner.fail(chalk.red(error.message));
41
+ if (error.message.includes('not in TEST stage') || error.message.includes('Run \'specsafe test\'')) {
42
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe test ${id}' to generate tests first.`));
43
+ }
44
+ else if (error.message.includes('not found')) {
45
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe new <name>' to create a spec first.`));
46
+ }
47
+ else if (error.message.includes('No test files')) {
48
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe test ${id}' to generate tests first.`));
49
+ }
50
+ process.exit(1);
51
+ }
52
+ });
53
+ //# sourceMappingURL=code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/commands/code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const completeCommand: Command;
3
+ //# sourceMappingURL=complete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../src/commands/complete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,eAAe,SAuIxB,CAAC"}
@@ -0,0 +1,137 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { Workflow, ProjectTracker, validateSpecId } from '@specsafe/core';
5
+ import { rename, readFile, access } from 'fs/promises';
6
+ import { join } from 'path';
7
+ export const completeCommand = new Command('complete')
8
+ .description('Complete spec (QA → COMPLETE)')
9
+ .argument('<id>', 'Spec ID')
10
+ .option('-r, --report <path>', 'Path to QA report')
11
+ .option('-n, --dry-run', 'Preview changes without writing files')
12
+ .action(async (id, options) => {
13
+ const spinner = ora(`Completing ${id}...`).start();
14
+ try {
15
+ // Validate spec ID format
16
+ validateSpecId(id);
17
+ const workflow = new Workflow();
18
+ const tracker = new ProjectTracker(process.cwd());
19
+ // Load existing specs from disk
20
+ await tracker.loadSpecsIntoWorkflow(workflow);
21
+ // Load QA report from file if provided
22
+ let qaReport;
23
+ if (options.report) {
24
+ const reportContent = await readFile(options.report, 'utf-8');
25
+ const parsedReport = JSON.parse(reportContent);
26
+ // Validate required fields
27
+ const requiredFields = ['id', 'specId', 'timestamp', 'recommendation', 'testResults', 'coverage', 'issues'];
28
+ const missingFields = requiredFields.filter(field => !(field in parsedReport));
29
+ if (missingFields.length > 0) {
30
+ throw new Error(`Invalid QA report: missing required fields: ${missingFields.join(', ')}`);
31
+ }
32
+ // Validate recommendation value
33
+ if (parsedReport.recommendation !== 'GO' && parsedReport.recommendation !== 'NO-GO') {
34
+ throw new Error(`Invalid QA report: recommendation must be 'GO' or 'NO-GO', got '${parsedReport.recommendation}'`);
35
+ }
36
+ qaReport = parsedReport;
37
+ // Convert timestamp from ISO string to Date object (JSON.parse produces strings)
38
+ if (typeof qaReport.timestamp === 'string') {
39
+ qaReport.timestamp = new Date(qaReport.timestamp);
40
+ }
41
+ }
42
+ else {
43
+ // Default QA report
44
+ qaReport = {
45
+ id: `QA-${id}`,
46
+ specId: id,
47
+ timestamp: new Date(),
48
+ testResults: [],
49
+ coverage: { statements: 100, branches: 100, functions: 100, lines: 100 },
50
+ recommendation: 'GO',
51
+ issues: [],
52
+ notes: 'All tests passing'
53
+ };
54
+ }
55
+ // Validate QA report
56
+ if (qaReport.specId !== id) {
57
+ throw new Error(`QA report spec ID (${qaReport.specId}) does not match target spec (${id})`);
58
+ }
59
+ if (qaReport.recommendation !== 'GO') {
60
+ throw new Error('Cannot complete: QA report recommends NO-GO. Address issues first.');
61
+ }
62
+ // Validate required fields are present
63
+ const requiredFields = ['id', 'specId', 'timestamp', 'recommendation', 'testResults', 'coverage', 'issues', 'notes'];
64
+ const missingFields = requiredFields.filter(field => !(field in qaReport));
65
+ if (missingFields.length > 0) {
66
+ throw new Error(`QA report is missing required fields: ${missingFields.join(', ')}`);
67
+ }
68
+ const sourcePath = join('specs', 'active', `${id}.md`);
69
+ const targetPath = join('specs', 'completed', `${id}.md`);
70
+ // Handle dry-run mode
71
+ if (options.dryRun) {
72
+ spinner.stop();
73
+ console.log(chalk.cyan('[DRY RUN] Would perform the following actions:\n'));
74
+ console.log(chalk.cyan(' State transition:'));
75
+ console.log(chalk.gray(` QA → COMPLETE`));
76
+ console.log(chalk.cyan(`\n File move:`));
77
+ console.log(chalk.gray(` ${sourcePath} → ${targetPath}`));
78
+ console.log(chalk.cyan(`\n QA Report:`));
79
+ console.log(chalk.gray(` ID: ${qaReport.id}`));
80
+ console.log(chalk.gray(` Recommendation: ${qaReport.recommendation}`));
81
+ console.log(chalk.gray(` Coverage: ${JSON.stringify(qaReport.coverage)}`));
82
+ console.log(chalk.cyan(`\n Would update PROJECT_STATE.md for spec: ${id}`));
83
+ process.exit(0);
84
+ }
85
+ // Move file FIRST, before updating state, to prevent inconsistent state on failure
86
+ try {
87
+ await access(sourcePath);
88
+ await rename(sourcePath, targetPath);
89
+ }
90
+ catch (renameError) {
91
+ if (renameError.code === 'ENOENT') {
92
+ // Source file doesn't exist — skip the move (spec may have been created without a file)
93
+ spinner.text = `Spec file not found at ${sourcePath}, skipping file move...`;
94
+ }
95
+ else {
96
+ throw new Error(`Failed to move spec file: ${renameError.message}`);
97
+ }
98
+ }
99
+ // Now update workflow state and persist
100
+ try {
101
+ workflow.moveToComplete(id, qaReport);
102
+ }
103
+ catch (moveError) {
104
+ if (moveError.message.includes('not found')) {
105
+ throw new Error(`Spec '${id}' not found. Run 'specsafe spec ${id}' to create it first.`);
106
+ }
107
+ if (moveError.message.includes('Must be in QA stage')) {
108
+ throw new Error(`Spec '${id}' is not in QA stage. Run 'specsafe qa ${id}' first.`);
109
+ }
110
+ if (moveError.message.includes('NO-GO')) {
111
+ throw new Error(`Cannot complete: QA report recommends NO-GO. Fix issues and re-run 'specsafe qa ${id}' first.`);
112
+ }
113
+ throw moveError;
114
+ }
115
+ await tracker.addSpec(workflow.getSpec(id));
116
+ spinner.succeed(chalk.green(`✅ Completed ${id}`));
117
+ console.log(chalk.blue('Spec moved to specs/completed/'));
118
+ console.log(chalk.green('Ready for production!'));
119
+ }
120
+ catch (error) {
121
+ spinner.fail(chalk.red(error.message));
122
+ if (error.message.includes('not in QA stage') || error.message.includes('Run \'specsafe qa\'')) {
123
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe qa ${id}' to run QA validation first.`));
124
+ }
125
+ else if (error.message.includes('NO-GO')) {
126
+ console.log(chalk.gray(`💡 Tip: Fix the failing tests and re-run 'specsafe qa ${id}' before completing.`));
127
+ }
128
+ else if (error.message.includes('not found')) {
129
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe new <name>' to create a spec first.`));
130
+ }
131
+ else if (error.message.includes('QA report')) {
132
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe qa ${id}' to generate a QA report first.`));
133
+ }
134
+ process.exit(1);
135
+ }
136
+ });
137
+ //# sourceMappingURL=complete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../src/commands/complete.ts"],"names":[],"mappings":"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"}