@specsafe/cli 0.8.0 ā 2.0.3
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/README.md +100 -279
- package/canonical/personas/bolt-zane.md +29 -0
- package/canonical/personas/forge-reva.md +29 -0
- package/canonical/personas/herald-cass.md +30 -0
- package/canonical/personas/mason-kai.md +30 -0
- package/canonical/personas/scout-elena.md +27 -0
- package/canonical/personas/warden-lyra.md +30 -0
- package/canonical/rules/.cursorrules.mdc +53 -0
- package/canonical/rules/.rules +48 -0
- package/canonical/rules/AGENTS.md +48 -0
- package/canonical/rules/CLAUDE.md +48 -0
- package/canonical/rules/CONVENTIONS.md +41 -0
- package/canonical/rules/GEMINI.md +50 -0
- package/canonical/rules/continue-config.yaml +5 -0
- package/canonical/skills/specsafe-archive/SKILL.md +63 -0
- package/canonical/skills/specsafe-code/SKILL.md +7 -0
- package/canonical/skills/specsafe-code/workflow.md +212 -0
- package/canonical/skills/specsafe-complete/SKILL.md +7 -0
- package/canonical/skills/specsafe-complete/workflow.md +130 -0
- package/canonical/skills/specsafe-doctor/SKILL.md +103 -0
- package/canonical/skills/specsafe-explore/SKILL.md +7 -0
- package/canonical/skills/specsafe-explore/workflow.md +100 -0
- package/canonical/skills/specsafe-init/SKILL.md +119 -0
- package/canonical/skills/specsafe-new/SKILL.md +7 -0
- package/canonical/skills/specsafe-new/workflow.md +156 -0
- package/canonical/skills/specsafe-qa/SKILL.md +7 -0
- package/canonical/skills/specsafe-qa/workflow.md +223 -0
- package/canonical/skills/specsafe-spec/SKILL.md +7 -0
- package/canonical/skills/specsafe-spec/workflow.md +158 -0
- package/canonical/skills/specsafe-status/SKILL.md +77 -0
- package/canonical/skills/specsafe-test/SKILL.md +7 -0
- package/canonical/skills/specsafe-test/workflow.md +210 -0
- package/canonical/skills/specsafe-verify/SKILL.md +7 -0
- package/canonical/skills/specsafe-verify/workflow.md +143 -0
- package/canonical/templates/project-state-template.md +33 -0
- package/canonical/templates/qa-report-template.md +55 -0
- package/canonical/templates/spec-template.md +52 -0
- package/canonical/templates/specsafe-config-template.json +10 -0
- package/generators/dist/adapters/aider.d.ts +2 -0
- package/generators/dist/adapters/aider.js +23 -0
- package/generators/dist/adapters/aider.js.map +1 -0
- package/generators/dist/adapters/antigravity.d.ts +2 -0
- package/generators/dist/adapters/antigravity.js +33 -0
- package/generators/dist/adapters/antigravity.js.map +1 -0
- package/generators/dist/adapters/claude-code.d.ts +2 -0
- package/generators/dist/adapters/claude-code.js +31 -0
- package/generators/dist/adapters/claude-code.js.map +1 -0
- package/generators/dist/adapters/continue.d.ts +2 -0
- package/generators/dist/adapters/continue.js +33 -0
- package/generators/dist/adapters/continue.js.map +1 -0
- package/generators/dist/adapters/cursor.d.ts +2 -0
- package/generators/dist/adapters/cursor.js +32 -0
- package/generators/dist/adapters/cursor.js.map +1 -0
- package/generators/dist/adapters/gemini.d.ts +2 -0
- package/generators/dist/adapters/gemini.js +36 -0
- package/generators/dist/adapters/gemini.js.map +1 -0
- package/generators/dist/adapters/index.d.ts +13 -0
- package/generators/dist/adapters/index.js +29 -0
- package/generators/dist/adapters/index.js.map +1 -0
- package/generators/dist/adapters/opencode.d.ts +2 -0
- package/generators/dist/adapters/opencode.js +32 -0
- package/generators/dist/adapters/opencode.js.map +1 -0
- package/generators/dist/adapters/types.d.ts +49 -0
- package/generators/dist/adapters/types.js +14 -0
- package/generators/dist/adapters/types.js.map +1 -0
- package/generators/dist/adapters/utils.d.ts +12 -0
- package/generators/dist/adapters/utils.js +68 -0
- package/generators/dist/adapters/utils.js.map +1 -0
- package/generators/dist/adapters/zed.d.ts +2 -0
- package/generators/dist/adapters/zed.js +31 -0
- package/generators/dist/adapters/zed.js.map +1 -0
- package/generators/dist/doctor.d.ts +10 -0
- package/generators/dist/doctor.js +123 -0
- package/generators/dist/doctor.js.map +1 -0
- package/generators/dist/index.d.ts +2 -0
- package/generators/dist/index.js +45 -0
- package/generators/dist/index.js.map +1 -0
- package/generators/dist/init.d.ts +9 -0
- package/generators/dist/init.js +167 -0
- package/generators/dist/init.js.map +1 -0
- package/generators/dist/install.d.ts +5 -0
- package/generators/dist/install.js +66 -0
- package/generators/dist/install.js.map +1 -0
- package/generators/dist/registry.d.ts +3 -0
- package/generators/dist/registry.js +8 -0
- package/generators/dist/registry.js.map +1 -0
- package/generators/dist/update.d.ts +5 -0
- package/generators/dist/update.js +40 -0
- package/generators/dist/update.js.map +1 -0
- package/package.json +31 -27
- package/dist/commands/apply.d.ts +0 -3
- package/dist/commands/apply.d.ts.map +0 -1
- package/dist/commands/apply.js +0 -182
- package/dist/commands/apply.js.map +0 -1
- package/dist/commands/archive.d.ts +0 -3
- package/dist/commands/archive.d.ts.map +0 -1
- package/dist/commands/archive.js +0 -99
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/capsule.d.ts +0 -8
- package/dist/commands/capsule.d.ts.map +0 -1
- package/dist/commands/capsule.js +0 -466
- package/dist/commands/capsule.js.map +0 -1
- package/dist/commands/complete.d.ts +0 -3
- package/dist/commands/complete.d.ts.map +0 -1
- package/dist/commands/complete.js +0 -140
- package/dist/commands/complete.js.map +0 -1
- package/dist/commands/constitution.d.ts +0 -3
- package/dist/commands/constitution.d.ts.map +0 -1
- package/dist/commands/constitution.js +0 -192
- package/dist/commands/constitution.js.map +0 -1
- package/dist/commands/create.d.ts +0 -10
- package/dist/commands/create.d.ts.map +0 -1
- package/dist/commands/create.js +0 -120
- package/dist/commands/create.js.map +0 -1
- package/dist/commands/delta.d.ts +0 -3
- package/dist/commands/delta.d.ts.map +0 -1
- package/dist/commands/delta.js +0 -82
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/diff.d.ts +0 -3
- package/dist/commands/diff.d.ts.map +0 -1
- package/dist/commands/diff.js +0 -102
- package/dist/commands/diff.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -3
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -204
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/done.d.ts +0 -3
- package/dist/commands/done.d.ts.map +0 -1
- package/dist/commands/done.js +0 -237
- package/dist/commands/done.js.map +0 -1
- package/dist/commands/explore.d.ts +0 -3
- package/dist/commands/explore.d.ts.map +0 -1
- package/dist/commands/explore.js +0 -236
- package/dist/commands/explore.js.map +0 -1
- package/dist/commands/export.d.ts +0 -7
- package/dist/commands/export.d.ts.map +0 -1
- package/dist/commands/export.js +0 -179
- package/dist/commands/export.js.map +0 -1
- package/dist/commands/extend.d.ts +0 -6
- package/dist/commands/extend.d.ts.map +0 -1
- package/dist/commands/extend.js +0 -167
- package/dist/commands/extend.js.map +0 -1
- package/dist/commands/init-old.d.ts +0 -3
- package/dist/commands/init-old.d.ts.map +0 -1
- package/dist/commands/init-old.js +0 -146
- package/dist/commands/init-old.js.map +0 -1
- package/dist/commands/init.d.ts +0 -3
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -298
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.d.ts +0 -3
- package/dist/commands/list.d.ts.map +0 -1
- package/dist/commands/list.js +0 -122
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/memory.d.ts +0 -3
- package/dist/commands/memory.d.ts.map +0 -1
- package/dist/commands/memory.js +0 -166
- package/dist/commands/memory.js.map +0 -1
- package/dist/commands/new.d.ts +0 -3
- package/dist/commands/new.d.ts.map +0 -1
- package/dist/commands/new.js +0 -508
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/qa.d.ts +0 -3
- package/dist/commands/qa.d.ts.map +0 -1
- package/dist/commands/qa.js +0 -179
- package/dist/commands/qa.js.map +0 -1
- package/dist/commands/rules.d.ts +0 -6
- package/dist/commands/rules.d.ts.map +0 -1
- package/dist/commands/rules.js +0 -232
- package/dist/commands/rules.js.map +0 -1
- package/dist/commands/shard.d.ts +0 -6
- package/dist/commands/shard.d.ts.map +0 -1
- package/dist/commands/shard.js +0 -199
- package/dist/commands/shard.js.map +0 -1
- package/dist/commands/spec.d.ts +0 -3
- package/dist/commands/spec.d.ts.map +0 -1
- package/dist/commands/spec.js +0 -302
- package/dist/commands/spec.js.map +0 -1
- package/dist/commands/status.d.ts +0 -3
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -47
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/test-apply.d.ts +0 -3
- package/dist/commands/test-apply.d.ts.map +0 -1
- package/dist/commands/test-apply.js +0 -228
- package/dist/commands/test-apply.js.map +0 -1
- package/dist/commands/test-create.d.ts +0 -3
- package/dist/commands/test-create.d.ts.map +0 -1
- package/dist/commands/test-create.js +0 -183
- package/dist/commands/test-create.js.map +0 -1
- package/dist/commands/test-guide.d.ts +0 -3
- package/dist/commands/test-guide.d.ts.map +0 -1
- package/dist/commands/test-guide.js +0 -190
- package/dist/commands/test-guide.js.map +0 -1
- package/dist/commands/test-report.d.ts +0 -3
- package/dist/commands/test-report.d.ts.map +0 -1
- package/dist/commands/test-report.js +0 -196
- package/dist/commands/test-report.js.map +0 -1
- package/dist/commands/test-submit.d.ts +0 -6
- package/dist/commands/test-submit.d.ts.map +0 -1
- package/dist/commands/test-submit.js +0 -236
- package/dist/commands/test-submit.js.map +0 -1
- package/dist/commands/verify.d.ts +0 -3
- package/dist/commands/verify.d.ts.map +0 -1
- package/dist/commands/verify.js +0 -288
- package/dist/commands/verify.js.map +0 -1
- package/dist/config.d.ts +0 -23
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -44
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -129
- package/dist/index.js.map +0 -1
- package/dist/rules/downloader.d.ts +0 -40
- package/dist/rules/downloader.d.ts.map +0 -1
- package/dist/rules/downloader.js +0 -253
- package/dist/rules/downloader.js.map +0 -1
- package/dist/rules/index.d.ts +0 -8
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js +0 -8
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/registry.d.ts +0 -45
- package/dist/rules/registry.d.ts.map +0 -1
- package/dist/rules/registry.js +0 -158
- package/dist/rules/registry.js.map +0 -1
- package/dist/rules/types.d.ts +0 -86
- package/dist/rules/types.d.ts.map +0 -1
- package/dist/rules/types.js +0 -6
- package/dist/rules/types.js.map +0 -1
- package/dist/utils/detectTools.d.ts +0 -15
- package/dist/utils/detectTools.d.ts.map +0 -1
- package/dist/utils/detectTools.js +0 -54
- package/dist/utils/detectTools.js.map +0 -1
- package/dist/utils/generateToolConfig.d.ts +0 -12
- package/dist/utils/generateToolConfig.d.ts.map +0 -1
- package/dist/utils/generateToolConfig.js +0 -1179
- package/dist/utils/generateToolConfig.js.map +0 -1
- package/dist/utils/testRunner.d.ts +0 -39
- package/dist/utils/testRunner.d.ts.map +0 -1
- package/dist/utils/testRunner.js +0 -325
- package/dist/utils/testRunner.js.map +0 -1
|
@@ -1,228 +0,0 @@
|
|
|
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 { readFile, writeFile, mkdir } from 'fs/promises';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { input, confirm, select } from '@inquirer/prompts';
|
|
8
|
-
export const testApplyCommand = new Command('test-apply')
|
|
9
|
-
.description('Start implementation with development guidance (TEST ā CODE)')
|
|
10
|
-
.argument('<id>', 'Spec ID')
|
|
11
|
-
.option('--skip-confirm', 'Skip confirmation prompts')
|
|
12
|
-
.option('--quick', 'Quick mode - minimal guidance')
|
|
13
|
-
.action(async (id, options) => {
|
|
14
|
-
const spinner = ora(`Starting implementation for ${id}...`).start();
|
|
15
|
-
try {
|
|
16
|
-
// Validate spec ID format
|
|
17
|
-
validateSpecId(id);
|
|
18
|
-
const workflow = new Workflow();
|
|
19
|
-
const tracker = new ProjectTracker(process.cwd());
|
|
20
|
-
// Load existing specs from disk
|
|
21
|
-
await tracker.loadSpecsIntoWorkflow(workflow);
|
|
22
|
-
// Check if spec exists
|
|
23
|
-
const spec = workflow.getSpec(id);
|
|
24
|
-
if (!spec) {
|
|
25
|
-
throw new Error(`Spec '${id}' not found. Run 'specsafe new <name>' to create it first.`);
|
|
26
|
-
}
|
|
27
|
-
// Read spec content for guidance
|
|
28
|
-
const specPath = join('specs/active', `${id}.md`);
|
|
29
|
-
let specContent;
|
|
30
|
-
try {
|
|
31
|
-
specContent = await readFile(specPath, 'utf-8');
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
throw new Error(`Spec file not found: ${specPath}`);
|
|
35
|
-
}
|
|
36
|
-
spinner.stop();
|
|
37
|
-
// Display development guidance
|
|
38
|
-
console.log(chalk.blue('\n' + 'ā'.repeat(60)));
|
|
39
|
-
console.log(chalk.bold(` Development Mode: ${id}`));
|
|
40
|
-
console.log(chalk.blue('ā'.repeat(60) + '\n'));
|
|
41
|
-
// Show current status
|
|
42
|
-
console.log(chalk.white(` Current Stage: ${spec.stage.toUpperCase()}`));
|
|
43
|
-
console.log(chalk.white(` Requirements: ${spec.requirements.length}`));
|
|
44
|
-
console.log(chalk.white(` Test Files: ${spec.testFiles.length}`));
|
|
45
|
-
console.log();
|
|
46
|
-
// Extract key information from spec
|
|
47
|
-
const prdSection = extractSection(specContent, 'Product Requirements Document');
|
|
48
|
-
const acceptanceCriteria = extractSection(specContent, 'Acceptance Criteria');
|
|
49
|
-
const technicalApproach = extractSection(specContent, 'Technical Approach');
|
|
50
|
-
// Show PRD highlights
|
|
51
|
-
if (prdSection) {
|
|
52
|
-
console.log(chalk.cyan(' š Problem Statement:'));
|
|
53
|
-
const problem = extractSection(prdSection, 'Problem Statement');
|
|
54
|
-
if (problem) {
|
|
55
|
-
const summary = problem.split('\n').find(line => line.trim() && !line.trim().startsWith('#'));
|
|
56
|
-
if (summary) {
|
|
57
|
-
console.log(chalk.gray(` ${summary.substring(0, 100)}${summary.length > 100 ? '...' : ''}`));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
console.log();
|
|
61
|
-
}
|
|
62
|
-
// Show acceptance criteria
|
|
63
|
-
if (acceptanceCriteria) {
|
|
64
|
-
console.log(chalk.cyan(' ā
Acceptance Criteria:'));
|
|
65
|
-
const criteria = acceptanceCriteria
|
|
66
|
-
.split('\n')
|
|
67
|
-
.filter(line => line.trim().startsWith('- [') || line.trim().startsWith('-'))
|
|
68
|
-
.slice(0, 5);
|
|
69
|
-
for (const criterion of criteria) {
|
|
70
|
-
const clean = criterion.replace(/^- \[[ x]\]\s*/i, '').trim();
|
|
71
|
-
console.log(chalk.gray(` ⢠${clean.substring(0, 60)}${clean.length > 60 ? '...' : ''}`));
|
|
72
|
-
}
|
|
73
|
-
console.log();
|
|
74
|
-
}
|
|
75
|
-
// Show technical approach hints
|
|
76
|
-
if (technicalApproach) {
|
|
77
|
-
console.log(chalk.cyan(' š ļø Technical Approach:'));
|
|
78
|
-
const techStack = extractSection(technicalApproach, 'Tech Stack');
|
|
79
|
-
if (techStack) {
|
|
80
|
-
console.log(chalk.gray(` ${techStack.split('\n').find(l => l.trim() && !l.trim().startsWith('#')) || 'See spec for details'}`));
|
|
81
|
-
}
|
|
82
|
-
console.log();
|
|
83
|
-
}
|
|
84
|
-
// Development guidance
|
|
85
|
-
if (!options.quick) {
|
|
86
|
-
console.log(chalk.cyan(' š Implementation Checklist:\n'));
|
|
87
|
-
const checklist = [
|
|
88
|
-
'Review test files to understand expected behavior',
|
|
89
|
-
'Create minimal implementation to pass first test',
|
|
90
|
-
'Iterate: run tests, fix, repeat',
|
|
91
|
-
'Refactor once all tests pass'
|
|
92
|
-
];
|
|
93
|
-
for (const item of checklist) {
|
|
94
|
-
console.log(chalk.gray(` [ ] ${item}`));
|
|
95
|
-
}
|
|
96
|
-
console.log();
|
|
97
|
-
}
|
|
98
|
-
// Interactive guidance
|
|
99
|
-
if (!options.skipConfirm && !options.quick) {
|
|
100
|
-
const wantGuidance = await confirm({
|
|
101
|
-
message: 'Would you like implementation guidance?',
|
|
102
|
-
default: true
|
|
103
|
-
});
|
|
104
|
-
if (wantGuidance) {
|
|
105
|
-
// Offer to create implementation file
|
|
106
|
-
const createFile = await confirm({
|
|
107
|
-
message: 'Create implementation file?',
|
|
108
|
-
default: true
|
|
109
|
-
});
|
|
110
|
-
if (createFile) {
|
|
111
|
-
const fileName = await input({
|
|
112
|
-
message: 'Implementation file name:',
|
|
113
|
-
default: `${id.toLowerCase().replace(/-/g, '_')}.ts`
|
|
114
|
-
});
|
|
115
|
-
const implPath = join('src', fileName);
|
|
116
|
-
// Generate boilerplate
|
|
117
|
-
const boilerplate = generateBoilerplate(spec, fileName);
|
|
118
|
-
await mkdir('src', { recursive: true });
|
|
119
|
-
await writeFile(implPath, boilerplate);
|
|
120
|
-
console.log(chalk.green(`\n ā
Created: ${implPath}`));
|
|
121
|
-
// Add to spec
|
|
122
|
-
spec.implementationFiles.push(implPath);
|
|
123
|
-
}
|
|
124
|
-
// Offer TDD approach guidance
|
|
125
|
-
const tddApproach = await select({
|
|
126
|
-
message: 'Choose your approach:',
|
|
127
|
-
choices: [
|
|
128
|
-
{ name: 'š Read tests first, then implement', value: 'read-first' },
|
|
129
|
-
{ name: 'ā” Start coding (I know what to do)', value: 'start-coding' },
|
|
130
|
-
{ name: 'šÆ One test at a time', value: 'one-test' }
|
|
131
|
-
]
|
|
132
|
-
});
|
|
133
|
-
switch (tddApproach) {
|
|
134
|
-
case 'read-first':
|
|
135
|
-
console.log(chalk.blue('\n š Recommended: Read the test files first'));
|
|
136
|
-
if (spec.testFiles.length > 0) {
|
|
137
|
-
console.log(chalk.gray(` ${spec.testFiles.join(', ')}`));
|
|
138
|
-
}
|
|
139
|
-
console.log(chalk.gray(' Understand what the tests expect, then implement.'));
|
|
140
|
-
break;
|
|
141
|
-
case 'one-test':
|
|
142
|
-
console.log(chalk.blue('\n šÆ Run tests with verbose output:'));
|
|
143
|
-
console.log(chalk.gray(` npx vitest run --reporter=verbose`));
|
|
144
|
-
console.log(chalk.gray(' Fix one failing test at a time.'));
|
|
145
|
-
break;
|
|
146
|
-
case 'start-coding':
|
|
147
|
-
console.log(chalk.blue('\n ā” Go for it! Remember:'));
|
|
148
|
-
console.log(chalk.gray(' - Run specsafe verify after changes'));
|
|
149
|
-
console.log(chalk.gray(' - Keep tests passing as you go'));
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Move to code stage (validates tests exist)
|
|
155
|
-
try {
|
|
156
|
-
workflow.moveToCode(id);
|
|
157
|
-
await tracker.addSpec(spec);
|
|
158
|
-
console.log(chalk.green(`\n ā
Moved ${id} to CODE stage`));
|
|
159
|
-
}
|
|
160
|
-
catch (moveError) {
|
|
161
|
-
if (moveError.message.includes('Must be in TEST stage')) {
|
|
162
|
-
// Already in code or beyond, that's fine
|
|
163
|
-
console.log(chalk.gray(`\n Note: Spec already in ${spec.stage.toUpperCase()} stage`));
|
|
164
|
-
}
|
|
165
|
-
else if (moveError.message.includes('No test files generated')) {
|
|
166
|
-
throw new Error(`No test files found. Run 'specsafe test ${id}' to generate tests first.`);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
throw moveError;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Development commands reference
|
|
173
|
-
console.log(chalk.blue('\n Development Commands:\n'));
|
|
174
|
-
console.log(chalk.gray(` Run tests: npx vitest run`));
|
|
175
|
-
console.log(chalk.gray(` Watch mode: npx vitest`));
|
|
176
|
-
console.log(chalk.gray(` Verify: specsafe verify ${id}`));
|
|
177
|
-
console.log(chalk.gray(` Check status: specsafe status`));
|
|
178
|
-
console.log(chalk.blue('\n' + 'ā'.repeat(60)));
|
|
179
|
-
console.log();
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
spinner.fail(chalk.red(error.message));
|
|
183
|
-
if (error.message.includes('not in TEST stage') || error.message.includes('Run \'specsafe test\'')) {
|
|
184
|
-
console.log(chalk.gray(`š” Tip: Run 'specsafe test ${id}' to generate tests first.`));
|
|
185
|
-
}
|
|
186
|
-
else if (error.message.includes('not found')) {
|
|
187
|
-
console.log(chalk.gray(`š” Tip: Run 'specsafe new <name>' to create a spec first.`));
|
|
188
|
-
}
|
|
189
|
-
else if (error.message.includes('No test files')) {
|
|
190
|
-
console.log(chalk.gray(`š” Tip: Run 'specsafe test ${id}' to generate tests first.`));
|
|
191
|
-
}
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
function extractSection(content, sectionName) {
|
|
196
|
-
const regex = new RegExp(`##+\s*${sectionName}[\\s\\S]*?(?=##+|$)`, 'i');
|
|
197
|
-
const match = content.match(regex);
|
|
198
|
-
return match ? match[0] : null;
|
|
199
|
-
}
|
|
200
|
-
function generateBoilerplate(spec, fileName) {
|
|
201
|
-
const functionName = fileName
|
|
202
|
-
.replace(/\.ts$/, '')
|
|
203
|
-
.replace(/[_-](.)/g, (_, char) => char.toUpperCase())
|
|
204
|
-
.replace(/^(.)/, (_, char) => char.toUpperCase());
|
|
205
|
-
const requirements = spec.requirements.map((r) => `// - ${r.text}`).join('\n');
|
|
206
|
-
return `/**
|
|
207
|
-
* ${spec.name}
|
|
208
|
-
* Spec: ${spec.id}
|
|
209
|
-
*
|
|
210
|
-
* Requirements:
|
|
211
|
-
${requirements || ' * (See spec for requirements)'}
|
|
212
|
-
*/
|
|
213
|
-
|
|
214
|
-
// TODO: Implement functionality to pass tests
|
|
215
|
-
|
|
216
|
-
export interface ${functionName}Options {
|
|
217
|
-
// Define options here
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export function ${functionName}(options?: ${functionName}Options): void {
|
|
221
|
-
// TODO: Implement
|
|
222
|
-
throw new Error('Not implemented');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// TODO: Add more exports as needed
|
|
226
|
-
`;
|
|
227
|
-
}
|
|
228
|
-
//# sourceMappingURL=test-apply.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-apply.js","sourceRoot":"","sources":["../../src/commands/test-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-create.d.ts","sourceRoot":"","sources":["../../src/commands/test-create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,eAAO,MAAM,iBAAiB,SAsM1B,CAAC"}
|
|
@@ -1,183 +0,0 @@
|
|
|
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 { TypeScriptTestGenerator, ScenarioParser } from '@specsafe/test-gen';
|
|
6
|
-
import { loadConfig } from '../config.js';
|
|
7
|
-
import { mkdir, writeFile, readFile, access } from 'fs/promises';
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import { confirm } from '@inquirer/prompts';
|
|
10
|
-
export const testCreateCommand = new Command('test-create')
|
|
11
|
-
.description('Generate tests from spec scenarios (SPEC ā TEST)')
|
|
12
|
-
.argument('<id>', 'Spec ID')
|
|
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')
|
|
16
|
-
.action(async (id, options) => {
|
|
17
|
-
const spinner = ora(`Generating tests for ${id}...`).start();
|
|
18
|
-
try {
|
|
19
|
-
// Validate spec ID format
|
|
20
|
-
validateSpecId(id);
|
|
21
|
-
const config = await loadConfig();
|
|
22
|
-
const workflow = new Workflow();
|
|
23
|
-
const tracker = new ProjectTracker(process.cwd());
|
|
24
|
-
// Use specified framework or fall back to config
|
|
25
|
-
const framework = options.framework || config.testFramework;
|
|
26
|
-
// Load existing specs from disk
|
|
27
|
-
await tracker.loadSpecsIntoWorkflow(workflow);
|
|
28
|
-
// Read spec file to extract scenarios
|
|
29
|
-
const specPath = join('specs', 'active', `${id}.md`);
|
|
30
|
-
let specContent;
|
|
31
|
-
try {
|
|
32
|
-
specContent = await readFile(specPath, 'utf-8');
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
throw new Error(`Spec file not found: ${specPath}`);
|
|
36
|
-
}
|
|
37
|
-
spinner.stop();
|
|
38
|
-
// Parse scenarios from spec
|
|
39
|
-
const parser = new ScenarioParser();
|
|
40
|
-
let scenarios = parser.parseScenarios(specContent);
|
|
41
|
-
// Also parse requirements
|
|
42
|
-
let requirements = parser.parseRequirements(specContent);
|
|
43
|
-
// Fall back to basic table parsing if ScenarioParser finds nothing
|
|
44
|
-
if (requirements.length === 0) {
|
|
45
|
-
const reqMatch = specContent.match(/###\s+Functional\s+Requirements[\s\S]*?(?=###|$)/i);
|
|
46
|
-
if (reqMatch) {
|
|
47
|
-
const rows = reqMatch[0].match(/\|\s*FR-\d+\s*\|[^|]+\|/g);
|
|
48
|
-
if (rows) {
|
|
49
|
-
requirements = rows.map(row => ({
|
|
50
|
-
id: row.match(/FR-\d+/)?.[0] || 'REQ-1',
|
|
51
|
-
text: row.split('|')[2]?.trim() || 'Requirement',
|
|
52
|
-
priority: 'P1',
|
|
53
|
-
scenarios: []
|
|
54
|
-
}));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
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
|
-
}
|
|
70
|
-
// Ensure spec exists in workflow
|
|
71
|
-
let spec = workflow.getSpec(id);
|
|
72
|
-
if (!spec) {
|
|
73
|
-
throw new Error(`Spec '${id}' not found in project state. Run 'specsafe spec ${id}' first.`);
|
|
74
|
-
}
|
|
75
|
-
// Update spec with parsed requirements
|
|
76
|
-
spec.requirements = requirements;
|
|
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
|
-
});
|
|
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`));
|
|
89
|
-
// Move to test stage (validates requirements exist)
|
|
90
|
-
try {
|
|
91
|
-
workflow.moveToTest(id);
|
|
92
|
-
}
|
|
93
|
-
catch (moveError) {
|
|
94
|
-
if (moveError.message.includes('not found')) {
|
|
95
|
-
throw new Error(`Spec '${id}' not found in project state. Run 'specsafe spec ${id}' first.`);
|
|
96
|
-
}
|
|
97
|
-
if (moveError.message.includes('Must be in SPEC stage')) {
|
|
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`));
|
|
100
|
-
}
|
|
101
|
-
if (moveError.message.includes('No requirements defined')) {
|
|
102
|
-
throw new Error(`Spec '${id}' has no requirements defined. Add requirements to the spec file first.`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Generate test code using the actual spec object
|
|
106
|
-
spinner.start('Generating test code...');
|
|
107
|
-
const generator = new TypeScriptTestGenerator({
|
|
108
|
-
framework: framework
|
|
109
|
-
});
|
|
110
|
-
const testCode = generator.generate(spec);
|
|
111
|
-
// Determine test file path
|
|
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
|
-
}
|
|
128
|
-
// Handle dry-run mode
|
|
129
|
-
if (options.dryRun) {
|
|
130
|
-
spinner.stop();
|
|
131
|
-
console.log(chalk.cyan('\n[DRY RUN] Would generate the following test file:\n'));
|
|
132
|
-
console.log(chalk.cyan(` ${testPath}`));
|
|
133
|
-
console.log(chalk.cyan(`\nTest code preview (first 30 lines):\n`));
|
|
134
|
-
const previewLines = testCode.split('\n').slice(0, 30).join('\n');
|
|
135
|
-
console.log(chalk.gray(previewLines));
|
|
136
|
-
if (testCode.split('\n').length > 30) {
|
|
137
|
-
console.log(chalk.gray(' ... (truncated)'));
|
|
138
|
-
}
|
|
139
|
-
console.log(chalk.cyan(`\nFramework: ${framework}`));
|
|
140
|
-
console.log(chalk.cyan(`Requirements: ${requirements.length}`));
|
|
141
|
-
console.log(chalk.cyan(`Scenarios: ${scenarios.length}`));
|
|
142
|
-
console.log(chalk.cyan(`Would update PROJECT_STATE.md for spec: ${id}`));
|
|
143
|
-
process.exit(0);
|
|
144
|
-
}
|
|
145
|
-
if (!shouldOverwrite) {
|
|
146
|
-
console.log(chalk.yellow('\nSkipped: Test file not modified'));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
// Write test file
|
|
150
|
-
await mkdir('tests', { recursive: true });
|
|
151
|
-
await writeFile(testPath, testCode);
|
|
152
|
-
// Update spec with test file reference
|
|
153
|
-
if (!spec.testFiles.includes(testPath)) {
|
|
154
|
-
spec.testFiles.push(testPath);
|
|
155
|
-
}
|
|
156
|
-
// Persist state
|
|
157
|
-
await tracker.addSpec(spec);
|
|
158
|
-
spinner.succeed(chalk.green(`Generated tests for ${id}`));
|
|
159
|
-
console.log(chalk.blue(`\n Test file: ${testPath}`));
|
|
160
|
-
console.log(chalk.blue(` Framework: ${framework}`));
|
|
161
|
-
console.log(chalk.blue(` Requirements: ${requirements.length}`));
|
|
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`));
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
spinner.fail(chalk.red(error.message));
|
|
170
|
-
if (error.message.includes('not found in project state') || error.message.includes('Run \'specsafe spec\'')) {
|
|
171
|
-
console.log(chalk.gray(`š” Tip: Run 'specsafe spec ${id}' to validate the spec first.`));
|
|
172
|
-
}
|
|
173
|
-
else if (error.message.includes('no requirements defined')) {
|
|
174
|
-
console.log(chalk.gray(`š” Tip: Add requirements to specs/active/${id}.md before generating tests.`));
|
|
175
|
-
}
|
|
176
|
-
else if (error.message.includes('Spec file not found')) {
|
|
177
|
-
console.log(chalk.gray(`š” Tip: Run 'specsafe new <name>' to create a spec first.`));
|
|
178
|
-
console.log(chalk.gray(` Expected file: specs/active/${id}.md`));
|
|
179
|
-
}
|
|
180
|
-
process.exit(1);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
//# sourceMappingURL=test-create.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-create.js","sourceRoot":"","sources":["../../src/commands/test-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-guide.d.ts","sourceRoot":"","sources":["../../src/commands/test-guide.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC,eAAO,MAAM,gBAAgB,SAuHzB,CAAC"}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import ora from 'ora';
|
|
4
|
-
import { Workflow, ProjectTracker, generateGuide, generateGuideContent, convertToPlaywrightScenarios, generatePlaywrightScript, } from '@specsafe/core';
|
|
5
|
-
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
6
|
-
import { join, dirname, normalize, isAbsolute } from 'path';
|
|
7
|
-
function validateSpecInput(specInput) {
|
|
8
|
-
if (specInput.endsWith('.md'))
|
|
9
|
-
return specInput;
|
|
10
|
-
if (!/^[A-Za-z0-9_-]+$/.test(specInput)) {
|
|
11
|
-
throw new Error('Invalid spec identifier. Use only letters, numbers, dash, and underscore.');
|
|
12
|
-
}
|
|
13
|
-
return specInput;
|
|
14
|
-
}
|
|
15
|
-
function sanitizeOutputPath(outputPath) {
|
|
16
|
-
const normalized = normalize(outputPath);
|
|
17
|
-
if (isAbsolute(normalized) || normalized.includes('..')) {
|
|
18
|
-
throw new Error('Output path must be a safe relative path');
|
|
19
|
-
}
|
|
20
|
-
return normalized;
|
|
21
|
-
}
|
|
22
|
-
export const testGuideCommand = new Command('test-guide')
|
|
23
|
-
.description('Generate E2E test guide from spec')
|
|
24
|
-
.argument('<spec>', 'Spec ID or path to spec file')
|
|
25
|
-
.option('-o, --output <file>', 'Output file path')
|
|
26
|
-
.option('--format <format>', 'Output format (markdown|json)', 'markdown')
|
|
27
|
-
.option('--mode <mode>', 'Automation mode (manual|playwright)', 'manual')
|
|
28
|
-
.option('--auto', 'Generate Playwright script output')
|
|
29
|
-
.option('--priority <priorities...>', 'Filter by priority (P0 P1 P2)', ['P0', 'P1', 'P2'])
|
|
30
|
-
.action(async (specInput, options) => {
|
|
31
|
-
const spinner = ora('Generating test guide...').start();
|
|
32
|
-
try {
|
|
33
|
-
specInput = validateSpecInput(specInput);
|
|
34
|
-
const workflow = new Workflow();
|
|
35
|
-
const tracker = new ProjectTracker(process.cwd());
|
|
36
|
-
await tracker.loadSpecsIntoWorkflow(workflow);
|
|
37
|
-
let specId;
|
|
38
|
-
let specContent;
|
|
39
|
-
let specPath;
|
|
40
|
-
if (specInput.endsWith('.md')) {
|
|
41
|
-
specPath = specInput;
|
|
42
|
-
try {
|
|
43
|
-
specContent = await readFile(specPath, 'utf-8');
|
|
44
|
-
specId = specInput.split('/').pop().replace('.md', '');
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
throw new Error(`Spec file not found: ${specPath}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
specId = specInput;
|
|
52
|
-
specPath = join('specs/active', `${specId}.md`);
|
|
53
|
-
try {
|
|
54
|
-
specContent = await readFile(specPath, 'utf-8');
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
throw new Error(`Spec file not found: ${specPath}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
let spec = workflow.getSpec(specId);
|
|
61
|
-
if (!spec)
|
|
62
|
-
spec = parseSpecFromContent(specId, specContent);
|
|
63
|
-
spinner.text = 'Generating test content...';
|
|
64
|
-
const priorityFilter = options.priority;
|
|
65
|
-
const mode = options.auto ? 'playwright' : (options.mode ?? 'manual');
|
|
66
|
-
const guide = generateGuide(spec, {
|
|
67
|
-
priorityFilter,
|
|
68
|
-
format: options.format,
|
|
69
|
-
mode,
|
|
70
|
-
});
|
|
71
|
-
let output;
|
|
72
|
-
let outputFormat;
|
|
73
|
-
if (options.auto) {
|
|
74
|
-
const scenarios = convertToPlaywrightScenarios(guide);
|
|
75
|
-
output = generatePlaywrightScript(spec.id, scenarios);
|
|
76
|
-
outputFormat = 'js';
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
const result = generateGuideContent(spec, {
|
|
80
|
-
priorityFilter,
|
|
81
|
-
format: options.format,
|
|
82
|
-
mode,
|
|
83
|
-
});
|
|
84
|
-
output = result.content;
|
|
85
|
-
outputFormat = result.format === 'markdown' ? 'md' : result.format;
|
|
86
|
-
}
|
|
87
|
-
spinner.stop();
|
|
88
|
-
let outputPath;
|
|
89
|
-
if (options.output) {
|
|
90
|
-
outputPath = sanitizeOutputPath(options.output);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
const guidesDir = join('.specsafe', 'e2e', 'guides');
|
|
94
|
-
await mkdir(guidesDir, { recursive: true });
|
|
95
|
-
const extension = outputFormat;
|
|
96
|
-
outputPath = join(guidesDir, `test-guide-${specId}.${extension}`);
|
|
97
|
-
}
|
|
98
|
-
await mkdir(dirname(outputPath), { recursive: true });
|
|
99
|
-
await writeFile(outputPath, output, 'utf-8');
|
|
100
|
-
console.log(chalk.green(`\nā
Test guide generated: ${outputPath}`));
|
|
101
|
-
console.log(chalk.blue('\nGuide Summary:'));
|
|
102
|
-
console.log(` Mode: ${mode}`);
|
|
103
|
-
console.log(` Scenarios: ${guide.scenarios.length}`);
|
|
104
|
-
console.log(` Steps: ${guide.scenarios.reduce((sum, s) => sum + s.steps.length, 0)}`);
|
|
105
|
-
console.log(` P0 (Critical): ${guide.scenarios.filter(s => s.priority === 'P0').length}`);
|
|
106
|
-
console.log(` P1 (High): ${guide.scenarios.filter(s => s.priority === 'P1').length}`);
|
|
107
|
-
console.log(` P2 (Normal): ${guide.scenarios.filter(s => s.priority === 'P2').length}`);
|
|
108
|
-
console.log(chalk.blue('\nNext steps:'));
|
|
109
|
-
if (mode === 'playwright') {
|
|
110
|
-
console.log(chalk.gray(` 1. Run generated script with: npx playwright test ${outputPath}`));
|
|
111
|
-
console.log(chalk.gray(' 2. Review screenshots captured at configured intervals'));
|
|
112
|
-
console.log(chalk.gray(' 3. Validate assertions and adjust selectors as needed'));
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
console.log(chalk.gray(` 1. Review the test guide: ${outputPath}`));
|
|
116
|
-
console.log(chalk.gray(' 2. Execute test scenarios manually'));
|
|
117
|
-
console.log(chalk.gray(' 3. Take screenshots at key steps'));
|
|
118
|
-
console.log(chalk.gray(` 4. Submit for analysis: specsafe test-submit ${specId} --screenshots ./my-screenshots/`));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
spinner.fail(chalk.red(error.message));
|
|
123
|
-
if (error.message.includes('not found')) {
|
|
124
|
-
console.log(chalk.gray(`\nš” Tip: Create a spec first with 'specsafe new <name>'`));
|
|
125
|
-
}
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
function parseSpecFromContent(specId, content) {
|
|
130
|
-
const safeContent = content.slice(0, 500_000);
|
|
131
|
-
const nameMatch = safeContent.match(/^#\s+(.+?)(?:\s+Specification)?$/m);
|
|
132
|
-
const name = nameMatch ? nameMatch[1] : specId;
|
|
133
|
-
const descMatch = safeContent.match(/## Overview\n+([\s\S]*?)(?=##|$)/);
|
|
134
|
-
const description = descMatch ? descMatch[1].trim() : '';
|
|
135
|
-
const requirements = [];
|
|
136
|
-
const tableMatch = safeContent.match(/\| ID \| Requirement \| Priority \|[^|]+\|\n\|[-\s|]+\|\n([\s\S]*?)(?=\n## |\n### |$)/);
|
|
137
|
-
if (tableMatch) {
|
|
138
|
-
const rows = tableMatch[1].trim().split('\n');
|
|
139
|
-
for (const row of rows) {
|
|
140
|
-
const cells = row.split('|').map(c => c.trim()).filter(Boolean);
|
|
141
|
-
if (cells.length >= 3) {
|
|
142
|
-
const [id, text, priority] = cells;
|
|
143
|
-
requirements.push({ id, text, priority: priority || 'P1', scenarios: [] });
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const scenariosMatch = safeContent.match(/## \d+\.\s*Scenarios\n+([\s\S]*?)(?=## \d+\.|$)/);
|
|
148
|
-
if (scenariosMatch) {
|
|
149
|
-
const scenarioBlocks = scenariosMatch[1].split(/### Scenario \d+:/);
|
|
150
|
-
for (let i = 0; i < scenarioBlocks.length; i++) {
|
|
151
|
-
const block = scenarioBlocks[i];
|
|
152
|
-
const givenMatch = block.match(/[-*]\s*\*\*Given\*\*\s*(.+)/i);
|
|
153
|
-
const whenMatch = block.match(/[-*]\s*\*\*When\*\*\s*(.+)/i);
|
|
154
|
-
const thenMatch = block.match(/[-*]\s*\*\*Then\*\*\s*(.+)/i);
|
|
155
|
-
if (givenMatch && whenMatch && thenMatch) {
|
|
156
|
-
const scenario = {
|
|
157
|
-
id: `SC-${i + 1}`,
|
|
158
|
-
given: givenMatch[1].trim(),
|
|
159
|
-
when: whenMatch[1].trim(),
|
|
160
|
-
thenOutcome: thenMatch[1].trim(),
|
|
161
|
-
};
|
|
162
|
-
if (requirements.length > 0)
|
|
163
|
-
requirements[0].scenarios.push(scenario);
|
|
164
|
-
else
|
|
165
|
-
requirements.push({ id: 'FR-1', text: 'Generated from scenarios', priority: 'P1', scenarios: [scenario] });
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (requirements.length === 0) {
|
|
170
|
-
requirements.push({
|
|
171
|
-
id: 'FR-1',
|
|
172
|
-
text: 'Feature implementation matches spec',
|
|
173
|
-
priority: 'P0',
|
|
174
|
-
scenarios: [{ id: 'SC-1', given: 'user accesses the feature', when: 'user interacts with the feature', thenOutcome: 'feature behaves as specified' }],
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
id: specId,
|
|
179
|
-
name,
|
|
180
|
-
description,
|
|
181
|
-
stage: 'spec',
|
|
182
|
-
createdAt: new Date(),
|
|
183
|
-
updatedAt: new Date(),
|
|
184
|
-
requirements,
|
|
185
|
-
testFiles: [],
|
|
186
|
-
implementationFiles: [],
|
|
187
|
-
metadata: { author: 'unknown', project: 'unknown', tags: [] },
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
//# sourceMappingURL=test-guide.js.map
|