@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.
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/commands/archive.d.ts +3 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +99 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/code.d.ts +3 -0
- package/dist/commands/code.d.ts.map +1 -0
- package/dist/commands/code.js +53 -0
- package/dist/commands/code.js.map +1 -0
- package/dist/commands/complete.d.ts +3 -0
- package/dist/commands/complete.d.ts.map +1 -0
- package/dist/commands/complete.js +137 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +204 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +80 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +122 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +141 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/qa.d.ts +3 -0
- package/dist/commands/qa.d.ts.map +1 -0
- package/dist/commands/qa.js +143 -0
- package/dist/commands/qa.js.map +1 -0
- package/dist/commands/spec.d.ts +3 -0
- package/dist/commands/spec.d.ts.map +1 -0
- package/dist/commands/spec.js +70 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +47 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +134 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- 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 @@
|
|
|
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":"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"}
|
|
@@ -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 @@
|
|
|
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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"}
|