@specsafe/cli 0.3.6 → 0.5.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/dist/commands/apply.d.ts +3 -0
- package/dist/commands/apply.d.ts.map +1 -0
- package/dist/commands/apply.js +182 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/complete.d.ts.map +1 -1
- package/dist/commands/complete.js +4 -1
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/delta.d.ts +3 -0
- package/dist/commands/delta.d.ts.map +1 -0
- package/dist/commands/delta.js +82 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +102 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/done.d.ts +3 -0
- package/dist/commands/done.d.ts.map +1 -0
- package/dist/commands/done.js +237 -0
- package/dist/commands/done.js.map +1 -0
- package/dist/commands/explore.d.ts +3 -0
- package/dist/commands/explore.d.ts.map +1 -0
- package/dist/commands/explore.js +236 -0
- package/dist/commands/explore.js.map +1 -0
- package/dist/commands/init-old.d.ts +3 -0
- package/dist/commands/init-old.d.ts.map +1 -0
- package/dist/commands/init-old.js +146 -0
- package/dist/commands/init-old.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +195 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +332 -29
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/qa.d.ts.map +1 -1
- package/dist/commands/qa.js +37 -1
- package/dist/commands/qa.js.map +1 -1
- package/dist/commands/rules.d.ts.map +1 -1
- package/dist/commands/rules.js +182 -97
- package/dist/commands/rules.js.map +1 -1
- package/dist/commands/spec.d.ts.map +1 -1
- package/dist/commands/spec.js +257 -25
- package/dist/commands/spec.js.map +1 -1
- package/dist/commands/test-apply.d.ts +3 -0
- package/dist/commands/test-apply.d.ts.map +1 -0
- package/dist/commands/test-apply.js +228 -0
- package/dist/commands/test-apply.js.map +1 -0
- package/dist/commands/test-create.d.ts +3 -0
- package/dist/commands/test-create.d.ts.map +1 -0
- package/dist/commands/{test.js → test-create.js} +79 -30
- package/dist/commands/test-create.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +288 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -6
- package/dist/index.js.map +1 -1
- package/dist/utils/generateToolConfig.d.ts +1 -1
- package/dist/utils/generateToolConfig.d.ts.map +1 -1
- package/dist/utils/generateToolConfig.js +980 -209
- package/dist/utils/generateToolConfig.js.map +1 -1
- package/dist/utils/testRunner.d.ts +39 -0
- package/dist/utils/testRunner.d.ts.map +1 -0
- package/dist/utils/testRunner.js +325 -0
- package/dist/utils/testRunner.js.map +1 -0
- package/package.json +3 -3
- package/dist/commands/code.d.ts +0 -3
- package/dist/commands/code.d.ts.map +0 -1
- package/dist/commands/code.js +0 -53
- package/dist/commands/code.js.map +0 -1
- package/dist/commands/test.d.ts +0 -3
- package/dist/commands/test.d.ts.map +0 -1
- 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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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: '
|
|
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
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
96
|
-
const previewLines = testCode.split('\n').slice(0,
|
|
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 >
|
|
136
|
+
if (testCode.split('\n').length > 30) {
|
|
99
137
|
console.log(chalk.gray(' ... (truncated)'));
|
|
100
138
|
}
|
|
101
|
-
console.log(chalk.cyan(`\nFramework: ${
|
|
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.
|
|
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(
|
|
115
|
-
console.log(chalk.blue(` Framework: ${
|
|
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(
|
|
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 @@
|
|
|
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":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
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,20 @@ 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 {
|
|
13
|
-
import {
|
|
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';
|
|
24
|
+
import { deltaCommand } from './commands/delta.js';
|
|
25
|
+
import { applyCommand } from './commands/apply.js';
|
|
26
|
+
import { diffCommand } from './commands/diff.js';
|
|
20
27
|
import { createRequire } from 'module';
|
|
21
28
|
const require = createRequire(import.meta.url);
|
|
22
29
|
const packageJson = require('../package.json');
|
|
@@ -24,20 +31,65 @@ const program = new Command();
|
|
|
24
31
|
program
|
|
25
32
|
.name('specsafe')
|
|
26
33
|
.description('SpecSafe - Test-Driven Development framework for AI-assisted software engineering')
|
|
27
|
-
.version(packageJson.version)
|
|
34
|
+
.version(packageJson.version)
|
|
35
|
+
.option('-v, --verbose', 'Enable verbose output');
|
|
28
36
|
// Add commands
|
|
29
37
|
program.addCommand(initCommand);
|
|
30
38
|
program.addCommand(newCommand);
|
|
31
39
|
program.addCommand(statusCommand);
|
|
32
40
|
program.addCommand(listCommand);
|
|
41
|
+
program.addCommand(exploreCommand); // NEW: Pre-spec exploration
|
|
33
42
|
program.addCommand(specCommand);
|
|
34
|
-
program.addCommand(
|
|
35
|
-
program.addCommand(
|
|
43
|
+
program.addCommand(testCreateCommand); // NEW: Generate tests from scenarios
|
|
44
|
+
program.addCommand(testApplyCommand); // NEW: Run tests, loop on failure
|
|
36
45
|
program.addCommand(qaCommand);
|
|
37
|
-
program.addCommand(
|
|
46
|
+
program.addCommand(verifyCommand); // NEW: Test runner with loop
|
|
47
|
+
program.addCommand(doneCommand); // NEW: Complete + archive
|
|
48
|
+
program.addCommand(completeCommand); // DEPRECATED: Use 'done' instead
|
|
38
49
|
program.addCommand(archiveCommand);
|
|
39
50
|
program.addCommand(doctorCommand);
|
|
40
51
|
program.addCommand(rulesCommand);
|
|
52
|
+
// Delta spec commands (brownfield)
|
|
53
|
+
program.addCommand(deltaCommand); // Create delta spec
|
|
54
|
+
program.addCommand(applyCommand); // Apply delta specs
|
|
55
|
+
program.addCommand(diffCommand); // Preview delta changes
|
|
56
|
+
// Workflow command group
|
|
57
|
+
program
|
|
58
|
+
.command('workflow')
|
|
59
|
+
.description('Show the SpecSafe workflow diagram')
|
|
60
|
+
.action(() => {
|
|
61
|
+
console.log(chalk.blue('\n┌─────────────────────────────────────────────────────────────┐'));
|
|
62
|
+
console.log(chalk.blue('│ SpecSafe Workflow │'));
|
|
63
|
+
console.log(chalk.blue('│ v0.4.0 - OpenSpec │'));
|
|
64
|
+
console.log(chalk.blue('└─────────────────────────────────────────────────────────────┘\n'));
|
|
65
|
+
console.log(chalk.white('Pre-Spec:'));
|
|
66
|
+
console.log(chalk.gray(' specsafe explore → Think through ideas before committing\n'));
|
|
67
|
+
console.log(chalk.white('Development Cycle:'));
|
|
68
|
+
console.log(chalk.cyan(' specsafe new [feature] → Create spec with PRD + BRD'));
|
|
69
|
+
console.log(chalk.gray(' ↓'));
|
|
70
|
+
console.log(chalk.cyan(' specsafe spec <id> → Flesh out detailed spec'));
|
|
71
|
+
console.log(chalk.gray(' ↓'));
|
|
72
|
+
console.log(chalk.cyan(' specsafe test-create <id> → Generate tests from scenarios'));
|
|
73
|
+
console.log(chalk.gray(' ↓'));
|
|
74
|
+
console.log(chalk.cyan(' specsafe test-apply <id> → Run tests, loop on failure'));
|
|
75
|
+
console.log(chalk.gray(' ↓ (if tests pass)'));
|
|
76
|
+
console.log(chalk.cyan(' specsafe qa <id> → QA validation'));
|
|
77
|
+
console.log(chalk.gray(' ↓'));
|
|
78
|
+
console.log(chalk.cyan(' specsafe done <id> → Complete & archive'));
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk.white('Brownfield Changes (Delta Specs):'));
|
|
81
|
+
console.log(chalk.cyan(' specsafe delta <id> → Create delta spec for changes'));
|
|
82
|
+
console.log(chalk.cyan(' specsafe diff <id> → Preview delta changes'));
|
|
83
|
+
console.log(chalk.cyan(' specsafe apply <id> → Apply delta to base spec'));
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(chalk.white('Utility Commands:'));
|
|
86
|
+
console.log(chalk.gray(' specsafe status → View project status'));
|
|
87
|
+
console.log(chalk.gray(' specsafe list → List all specs'));
|
|
88
|
+
console.log(chalk.gray(' specsafe doctor → Check project health'));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(chalk.yellow('💡 The verify command is the key differentiator:'));
|
|
91
|
+
console.log(chalk.yellow(' It runs tests and provides feedback for the dev loop.\n'));
|
|
92
|
+
});
|
|
41
93
|
// Global error handling
|
|
42
94
|
program.exitOverride();
|
|
43
95
|
(async () => {
|