@specsafe/cli 0.3.6 → 0.4.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 (49) hide show
  1. package/dist/commands/complete.d.ts.map +1 -1
  2. package/dist/commands/complete.js +4 -1
  3. package/dist/commands/complete.js.map +1 -1
  4. package/dist/commands/done.d.ts +3 -0
  5. package/dist/commands/done.d.ts.map +1 -0
  6. package/dist/commands/done.js +237 -0
  7. package/dist/commands/done.js.map +1 -0
  8. package/dist/commands/explore.d.ts +3 -0
  9. package/dist/commands/explore.d.ts.map +1 -0
  10. package/dist/commands/explore.js +236 -0
  11. package/dist/commands/explore.js.map +1 -0
  12. package/dist/commands/new.d.ts.map +1 -1
  13. package/dist/commands/new.js +312 -27
  14. package/dist/commands/new.js.map +1 -1
  15. package/dist/commands/spec.d.ts.map +1 -1
  16. package/dist/commands/spec.js +257 -25
  17. package/dist/commands/spec.js.map +1 -1
  18. package/dist/commands/test-apply.d.ts +3 -0
  19. package/dist/commands/test-apply.d.ts.map +1 -0
  20. package/dist/commands/test-apply.js +228 -0
  21. package/dist/commands/test-apply.js.map +1 -0
  22. package/dist/commands/test-create.d.ts +3 -0
  23. package/dist/commands/test-create.d.ts.map +1 -0
  24. package/dist/commands/{test.js → test-create.js} +79 -30
  25. package/dist/commands/test-create.js.map +1 -0
  26. package/dist/commands/verify.d.ts +3 -0
  27. package/dist/commands/verify.d.ts.map +1 -0
  28. package/dist/commands/verify.js +288 -0
  29. package/dist/commands/verify.js.map +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +46 -6
  33. package/dist/index.js.map +1 -1
  34. package/dist/utils/generateToolConfig.d.ts +1 -1
  35. package/dist/utils/generateToolConfig.d.ts.map +1 -1
  36. package/dist/utils/generateToolConfig.js +980 -209
  37. package/dist/utils/generateToolConfig.js.map +1 -1
  38. package/dist/utils/testRunner.d.ts +39 -0
  39. package/dist/utils/testRunner.d.ts.map +1 -0
  40. package/dist/utils/testRunner.js +325 -0
  41. package/dist/utils/testRunner.js.map +1 -0
  42. package/package.json +3 -3
  43. package/dist/commands/code.d.ts +0 -3
  44. package/dist/commands/code.d.ts.map +0 -1
  45. package/dist/commands/code.js +0 -53
  46. package/dist/commands/code.js.map +0 -1
  47. package/dist/commands/test.d.ts +0 -3
  48. package/dist/commands/test.d.ts.map +0 -1
  49. package/dist/commands/test.js.map +0 -1
@@ -4,12 +4,15 @@ import ora from 'ora';
4
4
  import { Workflow, ProjectTracker, validateSpecId } from '@specsafe/core';
5
5
  import { TypeScriptTestGenerator, ScenarioParser } from '@specsafe/test-gen';
6
6
  import { loadConfig } from '../config.js';
7
- import { mkdir, writeFile, readFile } from 'fs/promises';
7
+ import { mkdir, writeFile, readFile, access } from 'fs/promises';
8
8
  import { join } from 'path';
9
- export const testCommand = new Command('test')
10
- .description('Generate tests from spec (SPEC → TEST)')
9
+ import { confirm } from '@inquirer/prompts';
10
+ export const testCreateCommand = new Command('test-create')
11
+ .description('Generate tests from spec scenarios (SPEC → TEST)')
11
12
  .argument('<id>', 'Spec ID')
12
13
  .option('-n, --dry-run', 'Preview changes without writing files')
14
+ .option('--scenarios-only', 'Only generate tests for specific scenarios')
15
+ .option('--framework <framework>', 'Override test framework')
13
16
  .action(async (id, options) => {
14
17
  const spinner = ora(`Generating tests for ${id}...`).start();
15
18
  try {
@@ -18,6 +21,8 @@ export const testCommand = new Command('test')
18
21
  const config = await loadConfig();
19
22
  const workflow = new Workflow();
20
23
  const tracker = new ProjectTracker(process.cwd());
24
+ // Use specified framework or fall back to config
25
+ const framework = options.framework || config.testFramework;
21
26
  // Load existing specs from disk
22
27
  await tracker.loadSpecsIntoWorkflow(workflow);
23
28
  // Read spec file to extract scenarios
@@ -29,8 +34,11 @@ export const testCommand = new Command('test')
29
34
  catch {
30
35
  throw new Error(`Spec file not found: ${specPath}`);
31
36
  }
32
- // Use ScenarioParser to extract requirements from spec content
37
+ spinner.stop();
38
+ // Parse scenarios from spec
33
39
  const parser = new ScenarioParser();
40
+ let scenarios = parser.parseScenarios(specContent);
41
+ // Also parse requirements
34
42
  let requirements = parser.parseRequirements(specContent);
35
43
  // Fall back to basic table parsing if ScenarioParser finds nothing
36
44
  if (requirements.length === 0) {
@@ -41,12 +49,24 @@ export const testCommand = new Command('test')
41
49
  requirements = rows.map(row => ({
42
50
  id: row.match(/FR-\d+/)?.[0] || 'REQ-1',
43
51
  text: row.split('|')[2]?.trim() || 'Requirement',
44
- priority: 'P0',
52
+ priority: 'P1',
45
53
  scenarios: []
46
54
  }));
47
55
  }
48
56
  }
49
57
  }
58
+ // Parse manual scenarios from markdown
59
+ if (scenarios.length === 0) {
60
+ const scenarioMatches = specContent.matchAll(/###\s*Scenario\s*\d*:?\s*(.+?)\n[\s\S]*?\*\*Given\*\*\s*(.+?)\n[\s\S]*?\*\*When\*\*\s*(.+?)\n[\s\S]*?\*\*Then\*\*\s*(.+?)(?=\n###|$)/gi);
61
+ for (const match of scenarioMatches) {
62
+ scenarios.push({
63
+ id: `SC-${scenarios.length + 1}`,
64
+ given: match[2].trim(),
65
+ when: match[3].trim(),
66
+ thenOutcome: match[4].trim()
67
+ });
68
+ }
69
+ }
50
70
  // Ensure spec exists in workflow
51
71
  let spec = workflow.getSpec(id);
52
72
  if (!spec) {
@@ -54,19 +74,18 @@ export const testCommand = new Command('test')
54
74
  }
55
75
  // Update spec with parsed requirements
56
76
  spec.requirements = requirements;
57
- // Also parse inline scenarios from spec content
58
- const generator = new TypeScriptTestGenerator({
59
- framework: config.testFramework
60
- });
61
- const inlineScenarios = generator.parseScenarios(specContent);
62
- // Add inline scenarios to requirements if they exist and requirements lack scenarios
63
- if (inlineScenarios.length > 0 && spec.requirements.length > 0) {
64
- // Distribute scenarios across requirements, or add to first requirement
65
- const firstReq = spec.requirements[0];
66
- if (firstReq.scenarios.length === 0) {
67
- firstReq.scenarios = inlineScenarios;
68
- }
77
+ // Add scenarios to requirements
78
+ if (scenarios.length > 0 && spec.requirements.length > 0) {
79
+ // Distribute scenarios across requirements
80
+ scenarios.forEach((scenario, index) => {
81
+ const reqIndex = index % spec.requirements.length;
82
+ spec.requirements[reqIndex].scenarios.push(scenario);
83
+ });
69
84
  }
85
+ // Display what we found
86
+ console.log(chalk.blue(`\n📋 Found in spec:`));
87
+ console.log(chalk.gray(` ${requirements.length} requirements`));
88
+ console.log(chalk.gray(` ${scenarios.length} scenarios`));
70
89
  // Move to test stage (validates requirements exist)
71
90
  try {
72
91
  workflow.moveToTest(id);
@@ -76,45 +95,75 @@ export const testCommand = new Command('test')
76
95
  throw new Error(`Spec '${id}' not found in project state. Run 'specsafe spec ${id}' first.`);
77
96
  }
78
97
  if (moveError.message.includes('Must be in SPEC stage')) {
79
- throw new Error(`Spec '${id}' is not in SPEC stage. Run 'specsafe spec ${id}' first.`);
98
+ // Spec might already be in test stage or beyond, that's okay
99
+ console.log(chalk.gray(` Note: Spec is already in ${spec.stage.toUpperCase()} stage`));
80
100
  }
81
101
  if (moveError.message.includes('No requirements defined')) {
82
102
  throw new Error(`Spec '${id}' has no requirements defined. Add requirements to the spec file first.`);
83
103
  }
84
- throw moveError;
85
104
  }
86
105
  // Generate test code using the actual spec object
106
+ spinner.start('Generating test code...');
107
+ const generator = new TypeScriptTestGenerator({
108
+ framework: framework
109
+ });
87
110
  const testCode = generator.generate(spec);
88
111
  // Determine test file path
89
- const testPath = join('tests', `${id.toLowerCase().replace(/-/g, '_')}.test.ts`);
112
+ const testFileName = `${id.toLowerCase().replace(/-/g, '_')}.test.ts`;
113
+ const testPath = join('tests', testFileName);
114
+ // Check if test file already exists
115
+ let shouldOverwrite = true;
116
+ try {
117
+ await access(testPath);
118
+ spinner.stop();
119
+ shouldOverwrite = await confirm({
120
+ message: `Test file ${testPath} already exists. Overwrite?`,
121
+ default: false
122
+ });
123
+ spinner.start();
124
+ }
125
+ catch {
126
+ // File doesn't exist, proceed
127
+ }
90
128
  // Handle dry-run mode
91
129
  if (options.dryRun) {
92
130
  spinner.stop();
93
- console.log(chalk.cyan('[DRY RUN] Would generate the following test file:\n'));
131
+ console.log(chalk.cyan('\n[DRY RUN] Would generate the following test file:\n'));
94
132
  console.log(chalk.cyan(` ${testPath}`));
95
- console.log(chalk.cyan(`\nTest code preview (first 20 lines):\n`));
96
- const previewLines = testCode.split('\n').slice(0, 20).join('\n');
133
+ console.log(chalk.cyan(`\nTest code preview (first 30 lines):\n`));
134
+ const previewLines = testCode.split('\n').slice(0, 30).join('\n');
97
135
  console.log(chalk.gray(previewLines));
98
- if (testCode.split('\n').length > 20) {
136
+ if (testCode.split('\n').length > 30) {
99
137
  console.log(chalk.gray(' ... (truncated)'));
100
138
  }
101
- console.log(chalk.cyan(`\nFramework: ${config.testFramework}`));
139
+ console.log(chalk.cyan(`\nFramework: ${framework}`));
102
140
  console.log(chalk.cyan(`Requirements: ${requirements.length}`));
141
+ console.log(chalk.cyan(`Scenarios: ${scenarios.length}`));
103
142
  console.log(chalk.cyan(`Would update PROJECT_STATE.md for spec: ${id}`));
104
143
  process.exit(0);
105
144
  }
145
+ if (!shouldOverwrite) {
146
+ console.log(chalk.yellow('\nSkipped: Test file not modified'));
147
+ return;
148
+ }
106
149
  // Write test file
107
150
  await mkdir('tests', { recursive: true });
108
151
  await writeFile(testPath, testCode);
109
152
  // Update spec with test file reference
110
- spec.testFiles.push(testPath);
153
+ if (!spec.testFiles.includes(testPath)) {
154
+ spec.testFiles.push(testPath);
155
+ }
111
156
  // Persist state
112
157
  await tracker.addSpec(spec);
113
158
  spinner.succeed(chalk.green(`Generated tests for ${id}`));
114
- console.log(chalk.blue(` Test file: ${testPath}`));
115
- console.log(chalk.blue(` Framework: ${config.testFramework}`));
159
+ console.log(chalk.blue(`\n Test file: ${testPath}`));
160
+ console.log(chalk.blue(` Framework: ${framework}`));
116
161
  console.log(chalk.blue(` Requirements: ${requirements.length}`));
117
- console.log(chalk.blue('Next: Run specsafe code <id> to start implementation'));
162
+ console.log(chalk.blue(` Scenarios: ${scenarios.length}`));
163
+ console.log(chalk.gray('\n Next steps:'));
164
+ console.log(chalk.gray(` 1. Review and customize tests in ${testPath}`));
165
+ console.log(chalk.gray(` 2. Run: specsafe code ${id} → Start implementation`));
166
+ console.log(chalk.gray(` 3. Run: specsafe verify ${id} → Verify against tests`));
118
167
  }
119
168
  catch (error) {
120
169
  spinner.fail(chalk.red(error.message));
@@ -131,4 +180,4 @@ export const testCommand = new Command('test')
131
180
  process.exit(1);
132
181
  }
133
182
  });
134
- //# sourceMappingURL=test.js.map
183
+ //# sourceMappingURL=test-create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-create.js","sourceRoot":"","sources":["../../src/commands/test-create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const verifyCommand: Command;
3
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,eAAO,MAAM,aAAa,SAqItB,CAAC"}
@@ -0,0 +1,288 @@
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 { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import { access } from 'fs/promises';
8
+ import { join } from 'path';
9
+ import { loadConfig } from '../config.js';
10
+ const execAsync = promisify(exec);
11
+ export const verifyCommand = new Command('verify')
12
+ .description('Run tests and loop on failure (TEST → CODE loop)')
13
+ .argument('<id>', 'Spec ID')
14
+ .option('-w, --watch', 'Run in watch mode for continuous feedback')
15
+ .option('-u, --update', 'Update snapshots if applicable')
16
+ .option('--ci', 'Run in CI mode (no interactive)')
17
+ .action(async (id, options) => {
18
+ const spinner = ora(`Verifying ${id}...`).start();
19
+ try {
20
+ // Validate spec ID format
21
+ validateSpecId(id);
22
+ const workflow = new Workflow();
23
+ const tracker = new ProjectTracker(process.cwd());
24
+ const config = await loadConfig();
25
+ // Load existing specs from disk
26
+ await tracker.loadSpecsIntoWorkflow(workflow);
27
+ // Check if spec exists
28
+ const spec = workflow.getSpec(id);
29
+ if (!spec) {
30
+ throw new Error(`Spec '${id}' not found. Run 'specsafe new <name>' to create it first.`);
31
+ }
32
+ // Check if spec is in the right stage
33
+ if (spec.stage !== 'test' && spec.stage !== 'code' && spec.stage !== 'qa') {
34
+ throw new Error(`Spec '${id}' is in ${spec.stage.toUpperCase()} stage. Must be in TEST, CODE, or QA stage to verify.`);
35
+ }
36
+ // Check for test files
37
+ if (spec.testFiles.length === 0) {
38
+ // Try to find test file automatically
39
+ const testPath = join('tests', `${id.toLowerCase().replace(/-/g, '_')}.test.ts`);
40
+ try {
41
+ await access(testPath);
42
+ spec.testFiles.push(testPath);
43
+ }
44
+ catch {
45
+ throw new Error(`No test files found for ${id}. Run 'specsafe test ${id}' to generate tests first.`);
46
+ }
47
+ }
48
+ spinner.text = `Running ${config.testFramework} tests...`;
49
+ // Run tests based on framework
50
+ let testResult;
51
+ try {
52
+ testResult = await runTests(config.testFramework, spec.testFiles, options);
53
+ }
54
+ catch (error) {
55
+ // Parse test output even if tests fail
56
+ testResult = parseTestOutput(error.stdout, error.stderr, config.testFramework);
57
+ }
58
+ spinner.stop();
59
+ // Display results
60
+ console.log('\n' + chalk.bold('━'.repeat(60)));
61
+ console.log(chalk.bold(` Test Results for ${id}`));
62
+ console.log(chalk.bold('━'.repeat(60)));
63
+ if (testResult.passed) {
64
+ console.log(chalk.green(`\n ✅ All tests passed!`));
65
+ console.log(chalk.green(` ${testResult.passedTests}/${testResult.totalTests} tests passed`));
66
+ console.log(chalk.gray(` Duration: ${testResult.duration}ms`));
67
+ // If in CODE stage, suggest moving to QA
68
+ if (spec.stage === 'code') {
69
+ console.log(chalk.blue(`\n 💡 Ready for QA! Run: specsafe qa ${id}`));
70
+ }
71
+ else if (spec.stage === 'qa') {
72
+ console.log(chalk.green(`\n 🎉 Ready to complete! Run: specsafe done ${id}`));
73
+ }
74
+ else {
75
+ console.log(chalk.blue(`\n 💡 Tests pass! Ready to implement. Run: specsafe code ${id}`));
76
+ }
77
+ console.log(chalk.bold('━'.repeat(60)));
78
+ return;
79
+ }
80
+ // Tests failed - show detailed output
81
+ console.log(chalk.red(`\n ❌ Tests failed`));
82
+ console.log(chalk.red(` ${testResult.failedTests}/${testResult.totalTests} tests failed`));
83
+ console.log(chalk.gray(` ${testResult.passedTests} passed, ${testResult.skippedTests} skipped`));
84
+ console.log(chalk.gray(` Duration: ${testResult.duration}ms`));
85
+ if (testResult.failures.length > 0) {
86
+ console.log(chalk.yellow(`\n Failed Tests:\n`));
87
+ for (const failure of testResult.failures.slice(0, 5)) {
88
+ console.log(chalk.red(` ✗ ${failure.name}`));
89
+ if (failure.file) {
90
+ console.log(chalk.gray(` File: ${failure.file}`));
91
+ }
92
+ console.log(chalk.gray(` ${failure.error.split('\n')[0]}`));
93
+ console.log();
94
+ }
95
+ if (testResult.failures.length > 5) {
96
+ console.log(chalk.gray(` ... and ${testResult.failures.length - 5} more failures`));
97
+ }
98
+ }
99
+ console.log(chalk.bold('━'.repeat(60)));
100
+ // Provide guidance for fixes
101
+ console.log(chalk.yellow('\n 🔧 Suggested fixes:\n'));
102
+ const suggestions = generateFixSuggestions(testResult.failures, spec);
103
+ for (const suggestion of suggestions) {
104
+ console.log(chalk.blue(` • ${suggestion}`));
105
+ }
106
+ console.log(chalk.gray(`\n 💡 Run 'specsafe code ${id}' to continue development`));
107
+ console.log(chalk.gray(` Then run 'specsafe verify ${id}' again`));
108
+ if (!options.ci) {
109
+ console.log(chalk.gray(`\n Or use --watch mode for continuous feedback:`));
110
+ console.log(chalk.gray(` specsafe verify ${id} --watch`));
111
+ }
112
+ // Exit with error code in CI mode
113
+ if (options.ci) {
114
+ process.exit(1);
115
+ }
116
+ }
117
+ catch (error) {
118
+ spinner.fail(chalk.red(error.message));
119
+ if (error.message.includes('not found')) {
120
+ console.log(chalk.gray(`💡 Tip: Run 'specsafe new <name>' to create a spec first.`));
121
+ }
122
+ else if (error.message.includes('stage')) {
123
+ console.log(chalk.gray(`💡 Tip: Current workflow: SPEC → TEST → CODE → QA → COMPLETE`));
124
+ }
125
+ process.exit(1);
126
+ }
127
+ });
128
+ async function runTests(framework, testFiles, options) {
129
+ const testFilePattern = testFiles.length > 0 ? testFiles.join(' ') : '';
130
+ let command;
131
+ switch (framework) {
132
+ case 'vitest':
133
+ command = options.watch
134
+ ? `npx vitest ${testFilePattern}`
135
+ : `npx vitest run ${testFilePattern}`;
136
+ if (options.update)
137
+ command += ' --update';
138
+ break;
139
+ case 'jest':
140
+ command = `npx jest ${testFilePattern}`;
141
+ if (options.watch)
142
+ command += ' --watch';
143
+ if (options.update)
144
+ command += ' --updateSnapshot';
145
+ break;
146
+ case 'playwright':
147
+ command = `npx playwright test ${testFilePattern}`;
148
+ if (options.watch)
149
+ command += ' --ui';
150
+ break;
151
+ default:
152
+ // Try to detect from package.json
153
+ command = `npm test -- ${testFilePattern}`;
154
+ if (options.watch)
155
+ command += ' --watch';
156
+ }
157
+ try {
158
+ const { stdout, stderr } = await execAsync(command, {
159
+ timeout: options.watch ? undefined : 120000,
160
+ env: { ...process.env, CI: options.watch ? 'false' : 'true' }
161
+ });
162
+ return parseTestOutput(stdout, stderr, framework);
163
+ }
164
+ catch (error) {
165
+ // Test command failed - parse the output anyway
166
+ if (error.stdout || error.stderr) {
167
+ return parseTestOutput(error.stdout || '', error.stderr || '', framework);
168
+ }
169
+ throw error;
170
+ }
171
+ }
172
+ function parseTestOutput(stdout, stderr, framework) {
173
+ const output = stdout + stderr;
174
+ const result = {
175
+ passed: false,
176
+ totalTests: 0,
177
+ passedTests: 0,
178
+ failedTests: 0,
179
+ skippedTests: 0,
180
+ duration: 0,
181
+ failures: []
182
+ };
183
+ if (framework === 'vitest' || output.includes('vitest')) {
184
+ // Parse Vitest output
185
+ const passMatch = output.match(/(\d+) passed/);
186
+ const failMatch = output.match(/(\d+) failed/);
187
+ const skipMatch = output.match(/(\d+) skipped/);
188
+ const totalMatch = output.match(/Test Files?\s+\d+ passed \((\d+) total\)/);
189
+ const timeMatch = output.match(/Duration\s+(\d+)ms/);
190
+ if (passMatch)
191
+ result.passedTests = parseInt(passMatch[1], 10);
192
+ if (failMatch)
193
+ result.failedTests = parseInt(failMatch[1], 10);
194
+ if (skipMatch)
195
+ result.skippedTests = parseInt(skipMatch[1], 10);
196
+ if (timeMatch)
197
+ result.duration = parseInt(timeMatch[1], 10);
198
+ // Parse failures
199
+ const failureBlocks = output.match(/FAIL\s+[\s\S]+?(?=FAIL|Test Files|✓|passed|failed|$)/g) || [];
200
+ for (const block of failureBlocks) {
201
+ const testMatch = block.match(/FAIL\s+(.+)/);
202
+ const errorMatch = block.match(/AssertionError:\s*(.+)/) || block.match(/Error:\s*(.+)/);
203
+ if (testMatch) {
204
+ result.failures.push({
205
+ name: testMatch[1].trim(),
206
+ error: errorMatch ? errorMatch[1].trim() : 'Test failed',
207
+ file: testMatch[1].trim().split(' ')[0]
208
+ });
209
+ }
210
+ }
211
+ result.totalTests = result.passedTests + result.failedTests + result.skippedTests;
212
+ result.passed = result.failedTests === 0 && result.totalTests > 0;
213
+ }
214
+ else if (framework === 'jest' || output.includes('jest')) {
215
+ // Parse Jest output
216
+ const passedMatch = output.match(/(\d+)\s+passed/);
217
+ const failedMatch = output.match(/(\d+)\s+failed/);
218
+ const skippedMatch = output.match(/(\d+)\s+skipped/);
219
+ const totalMatch = output.match(/(\d+)\s+total/);
220
+ if (passedMatch)
221
+ result.passedTests = parseInt(passedMatch[1], 10);
222
+ if (failedMatch)
223
+ result.failedTests = parseInt(failedMatch[1], 10);
224
+ if (skippedMatch)
225
+ result.skippedTests = parseInt(skippedMatch[1], 10);
226
+ if (totalMatch) {
227
+ result.totalTests = parseInt(totalMatch[1], 10);
228
+ }
229
+ else {
230
+ result.totalTests = result.passedTests + result.failedTests + result.skippedTests;
231
+ }
232
+ const timeMatch = output.match(/Time:\s+([\d.]+)\s*s/);
233
+ if (timeMatch)
234
+ result.duration = Math.round(parseFloat(timeMatch[1]) * 1000);
235
+ result.passed = result.failedTests === 0 && result.totalTests > 0;
236
+ // Parse Jest failures
237
+ const failMatches = output.matchAll(/✕\s+(.+)\s+\((.+?)\)/g);
238
+ for (const match of failMatches) {
239
+ result.failures.push({
240
+ name: match[1].trim(),
241
+ error: 'Test failed',
242
+ file: match[2].trim()
243
+ });
244
+ }
245
+ }
246
+ else {
247
+ // Generic parsing
248
+ const passed = output.match(/passed|✓|success/gi);
249
+ const failed = output.match(/failed|✕|error/gi);
250
+ result.passed = !!(!failed || (passed && !failed));
251
+ result.passedTests = passed ? passed.length : 0;
252
+ result.failedTests = failed ? failed.length : 0;
253
+ result.totalTests = result.passedTests + result.failedTests;
254
+ }
255
+ return result;
256
+ }
257
+ function generateFixSuggestions(failures, spec) {
258
+ const suggestions = [];
259
+ if (failures.length === 0) {
260
+ suggestions.push('No specific failures detected. Check test output above.');
261
+ return suggestions;
262
+ }
263
+ // Analyze failure patterns
264
+ const hasAssertionErrors = failures.some(f => f.error.toLowerCase().includes('expected') ||
265
+ f.error.toLowerCase().includes('assertion'));
266
+ const hasMissingImports = failures.some(f => f.error.toLowerCase().includes('cannot find module') ||
267
+ f.error.toLowerCase().includes('cannot resolve'));
268
+ const hasTypeErrors = failures.some(f => f.error.toLowerCase().includes('type') ||
269
+ f.error.toLowerCase().includes('property'));
270
+ if (hasMissingImports) {
271
+ suggestions.push('Missing imports detected. Check that all dependencies are installed: npm install');
272
+ }
273
+ if (hasTypeErrors) {
274
+ suggestions.push('Type errors found. Run: npx tsc --noEmit to see full type errors');
275
+ }
276
+ if (hasAssertionErrors) {
277
+ suggestions.push('Test assertions failing. Review the expected vs actual values above.');
278
+ suggestions.push('Check if implementation matches the spec requirements in ' + spec.id);
279
+ }
280
+ if (!hasMissingImports && !hasTypeErrors && !hasAssertionErrors) {
281
+ suggestions.push('Review the spec file: specs/active/' + spec.id + '.md');
282
+ suggestions.push('Ensure implementation covers all acceptance criteria');
283
+ suggestions.push('Check that test setup/teardown is correct');
284
+ }
285
+ suggestions.push('Run individual test: npx vitest run --reporter=verbose ' + spec.testFiles[0]);
286
+ return suggestions;
287
+ }
288
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.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,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAY,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAgBlC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,kDAAkD,CAAC;KAC/D,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;KAC3B,MAAM,CAAC,aAAa,EAAE,2CAA2C,CAAC;KAClE,MAAM,CAAC,cAAc,EAAE,gCAAgC,CAAC;KACxD,MAAM,CAAC,MAAM,EAAE,iCAAiC,CAAC;KACjD,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,OAA4D,EAAE,EAAE;IACzF,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;QAClD,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAElC,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,MAAM,IAAI,KAAK,CAAC,SAAS,EAAE,4DAA4D,CAAC,CAAC;QAC3F,CAAC;QAED,sCAAsC;QACtC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,uDAAuD,CAAC,CAAC;QACzH,CAAC;QAED,uBAAuB;QACvB,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,sCAAsC;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;YACjF,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACvB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,2BAA2B,EAAE,wBAAwB,EAAE,4BAA4B,CAAC,CAAC;YACvG,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,GAAG,WAAW,MAAM,CAAC,aAAa,WAAW,CAAC;QAE1D,+BAA+B;QAC/B,IAAI,UAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,UAAU,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,uCAAuC;YACvC,UAAU,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACjF,CAAC;QAED,OAAO,CAAC,IAAI,EAAE,CAAC;QAEf,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAExC,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,UAAU,eAAe,CAAC,CAAC,CAAC;YACjG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,UAAU,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;YAEnE,yCAAyC;YACzC,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;gBAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,yCAAyC,EAAE,EAAE,CAAC,CAAC,CAAC;YACzE,CAAC;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,gDAAgD,EAAE,EAAE,CAAC,CAAC,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6DAA6D,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7F,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACxC,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,UAAU,eAAe,CAAC,CAAC,CAAC;QAC/F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,UAAU,CAAC,WAAW,YAAY,UAAU,CAAC,YAAY,UAAU,CAAC,CAAC,CAAC;QACrG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,UAAU,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;QAEnE,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC;YACjD,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC9C,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;oBACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvD,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,CAAC;YAED,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;YACvF,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAExC,6BAA6B;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAEvD,MAAM,WAAW,GAAG,sBAAsB,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtE,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,EAAE,2BAA2B,CAAC,CAAC,CAAC;QACpF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,kCAAkC,EAAE,SAAS,CAAC,CAAC,CAAC;QAEvE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC,CAAC;YAC/E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,EAAE,UAAU,CAAC,CAAC,CAAC;QAChE,CAAC;QAED,kCAAkC;QAClC,IAAI,OAAO,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IAEH,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC,CAAC;QACvF,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,KAAK,UAAU,QAAQ,CACrB,SAAiB,EACjB,SAAmB,EACnB,OAA8C;IAE9C,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,IAAI,OAAe,CAAC;IAEpB,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,GAAG,OAAO,CAAC,KAAK;gBACrB,CAAC,CAAC,cAAc,eAAe,EAAE;gBACjC,CAAC,CAAC,kBAAkB,eAAe,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,MAAM;gBAAE,OAAO,IAAI,WAAW,CAAC;YAC3C,MAAM;QACR,KAAK,MAAM;YACT,OAAO,GAAG,YAAY,eAAe,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,KAAK;gBAAE,OAAO,IAAI,UAAU,CAAC;YACzC,IAAI,OAAO,CAAC,MAAM;gBAAE,OAAO,IAAI,mBAAmB,CAAC;YACnD,MAAM;QACR,KAAK,YAAY;YACf,OAAO,GAAG,uBAAuB,eAAe,EAAE,CAAC;YACnD,IAAI,OAAO,CAAC,KAAK;gBAAE,OAAO,IAAI,OAAO,CAAC;YACtC,MAAM;QACR;YACE,kCAAkC;YAClC,OAAO,GAAG,eAAe,eAAe,EAAE,CAAC;YAC3C,IAAI,OAAO,CAAC,KAAK;gBAAE,OAAO,IAAI,UAAU,CAAC;IAC7C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE;YAClD,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;YAC3C,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE;SAC9D,CAAC,CAAC;QACH,OAAO,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,gDAAgD;QAChD,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,OAAO,eAAe,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,SAAS,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,MAAc,EAAE,SAAiB;IACxE,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAE/B,MAAM,MAAM,GAAe;QACzB,MAAM,EAAE,KAAK;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,IAAI,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,sBAAsB;QACtB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC5E,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAErD,IAAI,SAAS;YAAE,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,SAAS;YAAE,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,SAAS;YAAE,MAAM,CAAC,YAAY,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,IAAI,SAAS;YAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5D,iBAAiB;QACjB,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,uDAAuD,CAAC,IAAI,EAAE,CAAC;QAClG,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAEzF,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;oBACnB,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;oBACzB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,aAAa;oBACxD,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;iBACxC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;QAClF,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;IACpE,CAAC;SAAM,IAAI,SAAS,KAAK,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3D,oBAAoB;QACpB,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAEjD,IAAI,WAAW;YAAE,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,WAAW;YAAE,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,YAAY;YAAE,MAAM,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;QACpF,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACvD,IAAI,SAAS;YAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAE7E,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;QAElE,sBAAsB;QACtB,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;QAC7D,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACnB,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBACrB,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,CAAC;QACN,kBAAkB;QAClB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAEhD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACnD,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IAC9D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAgC,EAAE,IAAS;IACzE,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;QAC5E,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,2BAA2B;IAC3B,MAAM,kBAAkB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC3C,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC1C,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAC5C,CAAC;IAEF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC1C,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACpD,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CACjD,CAAC;IAEF,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CACtC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAC3C,CAAC;IAEF,IAAI,iBAAiB,EAAE,CAAC;QACtB,WAAW,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IACvG,CAAC;IAED,IAAI,aAAa,EAAE,CAAC;QAClB,WAAW,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,kBAAkB,EAAE,CAAC;QACvB,WAAW,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QACzF,WAAW,CAAC,IAAI,CAAC,2DAA2D,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,CAAC,iBAAiB,IAAI,CAAC,aAAa,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAChE,WAAW,CAAC,IAAI,CAAC,qCAAqC,GAAG,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;QAC1E,WAAW,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;QACzE,WAAW,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAChE,CAAC;IAED,WAAW,CAAC,IAAI,CAAC,yDAAyD,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhG,OAAO,WAAW,CAAC;AACrB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * SpecSafe CLI
4
4
  * Command-line interface for the SpecSafe TDD framework
5
+ * Version 0.4.0 - OpenSpec-style workflow
5
6
  */
6
7
  export {};
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG"}
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * SpecSafe CLI
4
4
  * Command-line interface for the SpecSafe TDD framework
5
+ * Version 0.4.0 - OpenSpec-style workflow
5
6
  */
6
7
  import { Command } from 'commander';
7
8
  import chalk from 'chalk';
@@ -9,14 +10,17 @@ import { initCommand } from './commands/init.js';
9
10
  import { newCommand } from './commands/new.js';
10
11
  import { statusCommand } from './commands/status.js';
11
12
  import { specCommand } from './commands/spec.js';
12
- import { testCommand } from './commands/test.js';
13
- import { codeCommand } from './commands/code.js';
13
+ import { testCreateCommand } from './commands/test-create.js';
14
+ import { testApplyCommand } from './commands/test-apply.js';
14
15
  import { qaCommand } from './commands/qa.js';
16
+ import { doneCommand } from './commands/done.js';
15
17
  import { completeCommand } from './commands/complete.js';
16
18
  import { listCommand } from './commands/list.js';
17
19
  import { archiveCommand } from './commands/archive.js';
18
20
  import { doctorCommand } from './commands/doctor.js';
19
21
  import { rulesCommand } from './commands/rules.js';
22
+ import { verifyCommand } from './commands/verify.js';
23
+ import { exploreCommand } from './commands/explore.js';
20
24
  import { createRequire } from 'module';
21
25
  const require = createRequire(import.meta.url);
22
26
  const packageJson = require('../package.json');
@@ -24,20 +28,56 @@ const program = new Command();
24
28
  program
25
29
  .name('specsafe')
26
30
  .description('SpecSafe - Test-Driven Development framework for AI-assisted software engineering')
27
- .version(packageJson.version);
31
+ .version(packageJson.version)
32
+ .option('-v, --verbose', 'Enable verbose output');
28
33
  // Add commands
29
34
  program.addCommand(initCommand);
30
35
  program.addCommand(newCommand);
31
36
  program.addCommand(statusCommand);
32
37
  program.addCommand(listCommand);
38
+ program.addCommand(exploreCommand); // NEW: Pre-spec exploration
33
39
  program.addCommand(specCommand);
34
- program.addCommand(testCommand);
35
- program.addCommand(codeCommand);
40
+ program.addCommand(testCreateCommand); // NEW: Generate tests from scenarios
41
+ program.addCommand(testApplyCommand); // NEW: Run tests, loop on failure
36
42
  program.addCommand(qaCommand);
37
- program.addCommand(completeCommand);
43
+ program.addCommand(verifyCommand); // NEW: Test runner with loop
44
+ program.addCommand(doneCommand); // NEW: Complete + archive
45
+ program.addCommand(completeCommand); // DEPRECATED: Use 'done' instead
38
46
  program.addCommand(archiveCommand);
39
47
  program.addCommand(doctorCommand);
40
48
  program.addCommand(rulesCommand);
49
+ // Workflow command group
50
+ program
51
+ .command('workflow')
52
+ .description('Show the SpecSafe workflow diagram')
53
+ .action(() => {
54
+ console.log(chalk.blue('\n┌─────────────────────────────────────────────────────────────┐'));
55
+ console.log(chalk.blue('│ SpecSafe Workflow │'));
56
+ console.log(chalk.blue('│ v0.4.0 - OpenSpec │'));
57
+ console.log(chalk.blue('└─────────────────────────────────────────────────────────────┘\n'));
58
+ console.log(chalk.white('Pre-Spec:'));
59
+ console.log(chalk.gray(' specsafe explore → Think through ideas before committing\n'));
60
+ console.log(chalk.white('Development Cycle:'));
61
+ console.log(chalk.cyan(' specsafe new [feature] → Create spec with PRD + BRD'));
62
+ console.log(chalk.gray(' ↓'));
63
+ console.log(chalk.cyan(' specsafe spec <id> → Flesh out detailed spec'));
64
+ console.log(chalk.gray(' ↓'));
65
+ console.log(chalk.cyan(' specsafe test-create <id> → Generate tests from scenarios'));
66
+ console.log(chalk.gray(' ↓'));
67
+ console.log(chalk.cyan(' specsafe test-apply <id> → Run tests, loop on failure'));
68
+ console.log(chalk.gray(' ↓ (if tests pass)'));
69
+ console.log(chalk.cyan(' specsafe qa <id> → QA validation'));
70
+ console.log(chalk.gray(' ↓'));
71
+ console.log(chalk.cyan(' specsafe done <id> → Complete & archive'));
72
+ console.log();
73
+ console.log(chalk.white('Utility Commands:'));
74
+ console.log(chalk.gray(' specsafe status → View project status'));
75
+ console.log(chalk.gray(' specsafe list → List all specs'));
76
+ console.log(chalk.gray(' specsafe doctor → Check project health'));
77
+ console.log();
78
+ console.log(chalk.yellow('💡 The verify command is the key differentiator:'));
79
+ console.log(chalk.yellow(' It runs tests and provides feedback for the dev loop.\n'));
80
+ });
41
81
  // Global error handling
42
82
  program.exitOverride();
43
83
  (async () => {