@paths.design/caws-cli 7.0.2 → 8.0.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 (217) hide show
  1. package/dist/budget-derivation.js +5 -4
  2. package/dist/commands/diagnose.js +24 -19
  3. package/dist/commands/init.js +51 -4
  4. package/dist/commands/quality-gates.js +147 -9
  5. package/dist/commands/specs.js +148 -14
  6. package/dist/commands/status.js +2 -2
  7. package/dist/commands/tool.js +2 -4
  8. package/dist/config/index.js +17 -8
  9. package/dist/generators/working-spec.js +19 -6
  10. package/dist/scaffold/git-hooks.js +245 -46
  11. package/dist/scaffold/index.js +53 -7
  12. package/dist/templates/.caws/tools/README.md +21 -0
  13. package/dist/templates/.cursor/README.md +311 -0
  14. package/dist/templates/.cursor/hooks/audit.sh +55 -0
  15. package/dist/templates/.cursor/hooks/block-dangerous.sh +83 -0
  16. package/dist/templates/.cursor/hooks/caws-quality-check.sh +52 -0
  17. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +130 -0
  18. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
  19. package/dist/templates/.cursor/hooks/format.sh +38 -0
  20. package/dist/templates/.cursor/hooks/naming-check.sh +64 -0
  21. package/dist/templates/.cursor/hooks/scan-secrets.sh +46 -0
  22. package/dist/templates/.cursor/hooks/scope-guard.sh +52 -0
  23. package/dist/templates/.cursor/hooks/validate-spec.sh +83 -0
  24. package/dist/templates/.cursor/hooks.json +59 -0
  25. package/dist/templates/.cursor/rules/00-claims-verification.mdc +144 -0
  26. package/dist/templates/.cursor/rules/01-working-style.mdc +50 -0
  27. package/dist/templates/.cursor/rules/02-quality-gates.mdc +370 -0
  28. package/dist/templates/.cursor/rules/03-naming-and-refactor.mdc +33 -0
  29. package/dist/templates/.cursor/rules/04-logging-language-style.mdc +23 -0
  30. package/dist/templates/.cursor/rules/05-safe-defaults-guards.mdc +23 -0
  31. package/dist/templates/.cursor/rules/06-typescript-conventions.mdc +36 -0
  32. package/dist/templates/.cursor/rules/07-process-ops.mdc +20 -0
  33. package/dist/templates/.cursor/rules/08-solid-and-architecture.mdc +16 -0
  34. package/dist/templates/.cursor/rules/09-docstrings.mdc +89 -0
  35. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +390 -0
  36. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +385 -0
  37. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +516 -0
  38. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +588 -0
  39. package/dist/templates/.cursor/rules/README.md +148 -0
  40. package/dist/templates/.github/copilot/instructions.md +311 -0
  41. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
  42. package/dist/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
  43. package/dist/templates/.vscode/launch.json +56 -0
  44. package/dist/templates/.vscode/settings.json +93 -0
  45. package/dist/templates/.windsurf/workflows/caws-guided-development.md +92 -0
  46. package/dist/templates/COMMIT_CONVENTIONS.md +86 -0
  47. package/dist/templates/OIDC_SETUP.md +300 -0
  48. package/dist/templates/agents.md +1047 -0
  49. package/dist/templates/codemod/README.md +1 -0
  50. package/dist/templates/codemod/test.js +93 -0
  51. package/dist/templates/docs/README.md +150 -0
  52. package/dist/templates/scripts/quality-gates/check-god-objects.js +146 -0
  53. package/dist/templates/scripts/quality-gates/run-quality-gates.js +50 -0
  54. package/dist/templates/scripts/v3/analysis/todo_analyzer.py +1997 -0
  55. package/dist/tool-loader.js +6 -1
  56. package/dist/tool-validator.js +8 -2
  57. package/dist/utils/detection.js +4 -3
  58. package/dist/utils/git-lock.js +119 -0
  59. package/dist/utils/gitignore-updater.js +148 -0
  60. package/dist/utils/project-analysis.js +176 -16
  61. package/dist/utils/quality-gates.js +48 -7
  62. package/dist/utils/spec-resolver.js +27 -3
  63. package/dist/utils/yaml-validation.js +156 -0
  64. package/dist/validation/spec-validation.js +81 -2
  65. package/package.json +2 -2
  66. package/templates/.caws/schemas/waivers.schema.json +30 -0
  67. package/templates/.caws/schemas/working-spec.schema.json +133 -0
  68. package/templates/.caws/templates/working-spec.template.yml +74 -0
  69. package/templates/.caws/tools/README.md +21 -0
  70. package/templates/.caws/tools/scope-guard.js +208 -0
  71. package/templates/.caws/tools-allow.json +331 -0
  72. package/templates/.caws/waivers.yml +19 -0
  73. package/templates/.cursor/hooks/scope-guard.sh +2 -2
  74. package/templates/.cursor/hooks/validate-spec.sh +42 -7
  75. package/dist/budget-derivation.d.ts +0 -74
  76. package/dist/budget-derivation.d.ts.map +0 -1
  77. package/dist/cicd-optimizer.d.ts +0 -142
  78. package/dist/cicd-optimizer.d.ts.map +0 -1
  79. package/dist/commands/archive.d.ts +0 -50
  80. package/dist/commands/archive.d.ts.map +0 -1
  81. package/dist/commands/burnup.d.ts +0 -6
  82. package/dist/commands/burnup.d.ts.map +0 -1
  83. package/dist/commands/diagnose.d.ts +0 -52
  84. package/dist/commands/diagnose.d.ts.map +0 -1
  85. package/dist/commands/evaluate.d.ts +0 -8
  86. package/dist/commands/evaluate.d.ts.map +0 -1
  87. package/dist/commands/init.d.ts +0 -5
  88. package/dist/commands/init.d.ts.map +0 -1
  89. package/dist/commands/iterate.d.ts +0 -8
  90. package/dist/commands/iterate.d.ts.map +0 -1
  91. package/dist/commands/mode.d.ts +0 -24
  92. package/dist/commands/mode.d.ts.map +0 -1
  93. package/dist/commands/plan.d.ts +0 -49
  94. package/dist/commands/plan.d.ts.map +0 -1
  95. package/dist/commands/provenance.d.ts +0 -32
  96. package/dist/commands/provenance.d.ts.map +0 -1
  97. package/dist/commands/quality-gates.d.ts +0 -52
  98. package/dist/commands/quality-gates.d.ts.map +0 -1
  99. package/dist/commands/quality-monitor.d.ts +0 -17
  100. package/dist/commands/quality-monitor.d.ts.map +0 -1
  101. package/dist/commands/specs.d.ts +0 -71
  102. package/dist/commands/specs.d.ts.map +0 -1
  103. package/dist/commands/status.d.ts +0 -44
  104. package/dist/commands/status.d.ts.map +0 -1
  105. package/dist/commands/templates.d.ts +0 -74
  106. package/dist/commands/templates.d.ts.map +0 -1
  107. package/dist/commands/tool.d.ts +0 -13
  108. package/dist/commands/tool.d.ts.map +0 -1
  109. package/dist/commands/troubleshoot.d.ts +0 -8
  110. package/dist/commands/troubleshoot.d.ts.map +0 -1
  111. package/dist/commands/tutorial.d.ts +0 -55
  112. package/dist/commands/tutorial.d.ts.map +0 -1
  113. package/dist/commands/validate.d.ts +0 -15
  114. package/dist/commands/validate.d.ts.map +0 -1
  115. package/dist/commands/waivers.d.ts +0 -8
  116. package/dist/commands/waivers.d.ts.map +0 -1
  117. package/dist/commands/workflow.d.ts +0 -85
  118. package/dist/commands/workflow.d.ts.map +0 -1
  119. package/dist/config/index.d.ts +0 -29
  120. package/dist/config/index.d.ts.map +0 -1
  121. package/dist/config/modes.d.ts +0 -225
  122. package/dist/config/modes.d.ts.map +0 -1
  123. package/dist/constants/spec-types.d.ts +0 -41
  124. package/dist/constants/spec-types.d.ts.map +0 -1
  125. package/dist/error-handler.d.ts +0 -164
  126. package/dist/error-handler.d.ts.map +0 -1
  127. package/dist/generators/jest-config.d.ts +0 -32
  128. package/dist/generators/jest-config.d.ts.map +0 -1
  129. package/dist/generators/working-spec.d.ts +0 -13
  130. package/dist/generators/working-spec.d.ts.map +0 -1
  131. package/dist/index-new.d.ts +0 -5
  132. package/dist/index-new.d.ts.map +0 -1
  133. package/dist/index-new.js +0 -317
  134. package/dist/index.d.ts +0 -5
  135. package/dist/index.d.ts.map +0 -1
  136. package/dist/index.js.backup +0 -4711
  137. package/dist/minimal-cli.d.ts +0 -3
  138. package/dist/minimal-cli.d.ts.map +0 -1
  139. package/dist/policy/PolicyManager.d.ts +0 -104
  140. package/dist/policy/PolicyManager.d.ts.map +0 -1
  141. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  142. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  143. package/dist/scaffold/git-hooks.d.ts +0 -20
  144. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  145. package/dist/scaffold/index.d.ts +0 -20
  146. package/dist/scaffold/index.d.ts.map +0 -1
  147. package/dist/spec/SpecFileManager.d.ts +0 -146
  148. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  149. package/dist/test-analysis.d.ts +0 -182
  150. package/dist/test-analysis.d.ts.map +0 -1
  151. package/dist/tool-interface.d.ts +0 -236
  152. package/dist/tool-interface.d.ts.map +0 -1
  153. package/dist/tool-loader.d.ts +0 -77
  154. package/dist/tool-loader.d.ts.map +0 -1
  155. package/dist/tool-validator.d.ts +0 -72
  156. package/dist/tool-validator.d.ts.map +0 -1
  157. package/dist/utils/detection.d.ts +0 -7
  158. package/dist/utils/detection.d.ts.map +0 -1
  159. package/dist/utils/finalization.d.ts +0 -17
  160. package/dist/utils/finalization.d.ts.map +0 -1
  161. package/dist/utils/project-analysis.d.ts +0 -14
  162. package/dist/utils/project-analysis.d.ts.map +0 -1
  163. package/dist/utils/quality-gates.d.ts +0 -49
  164. package/dist/utils/quality-gates.d.ts.map +0 -1
  165. package/dist/utils/spec-resolver.d.ts +0 -88
  166. package/dist/utils/spec-resolver.d.ts.map +0 -1
  167. package/dist/utils/typescript-detector.d.ts +0 -63
  168. package/dist/utils/typescript-detector.d.ts.map +0 -1
  169. package/dist/validation/spec-validation.d.ts +0 -43
  170. package/dist/validation/spec-validation.d.ts.map +0 -1
  171. package/dist/waivers-manager.d.ts +0 -167
  172. package/dist/waivers-manager.d.ts.map +0 -1
  173. package/templates/apps/tools/caws/COMPLETION_REPORT.md +0 -331
  174. package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +0 -360
  175. package/templates/apps/tools/caws/README.md +0 -463
  176. package/templates/apps/tools/caws/TEST_STATUS.md +0 -365
  177. package/templates/apps/tools/caws/attest.js +0 -357
  178. package/templates/apps/tools/caws/ci-optimizer.js +0 -642
  179. package/templates/apps/tools/caws/config.ts +0 -245
  180. package/templates/apps/tools/caws/cross-functional.js +0 -876
  181. package/templates/apps/tools/caws/dashboard.js +0 -1112
  182. package/templates/apps/tools/caws/flake-detector.ts +0 -362
  183. package/templates/apps/tools/caws/gates.js +0 -198
  184. package/templates/apps/tools/caws/gates.ts +0 -271
  185. package/templates/apps/tools/caws/language-adapters.ts +0 -381
  186. package/templates/apps/tools/caws/language-support.d.ts +0 -367
  187. package/templates/apps/tools/caws/language-support.d.ts.map +0 -1
  188. package/templates/apps/tools/caws/language-support.js +0 -585
  189. package/templates/apps/tools/caws/legacy-assessment.ts +0 -408
  190. package/templates/apps/tools/caws/legacy-assessor.js +0 -764
  191. package/templates/apps/tools/caws/mutant-analyzer.js +0 -734
  192. package/templates/apps/tools/caws/perf-budgets.ts +0 -349
  193. package/templates/apps/tools/caws/prompt-lint.js.backup +0 -274
  194. package/templates/apps/tools/caws/property-testing.js +0 -707
  195. package/templates/apps/tools/caws/provenance.d.ts +0 -14
  196. package/templates/apps/tools/caws/provenance.d.ts.map +0 -1
  197. package/templates/apps/tools/caws/provenance.js +0 -132
  198. package/templates/apps/tools/caws/provenance.js.backup +0 -73
  199. package/templates/apps/tools/caws/provenance.ts +0 -211
  200. package/templates/apps/tools/caws/security-provenance.ts +0 -483
  201. package/templates/apps/tools/caws/shared/base-tool.ts +0 -281
  202. package/templates/apps/tools/caws/shared/config-manager.ts +0 -366
  203. package/templates/apps/tools/caws/shared/gate-checker.ts +0 -849
  204. package/templates/apps/tools/caws/shared/types.ts +0 -444
  205. package/templates/apps/tools/caws/shared/validator.ts +0 -305
  206. package/templates/apps/tools/caws/shared/waivers-manager.ts +0 -174
  207. package/templates/apps/tools/caws/spec-test-mapper.ts +0 -391
  208. package/templates/apps/tools/caws/test-quality.js +0 -578
  209. package/templates/apps/tools/caws/validate.js +0 -76
  210. package/templates/apps/tools/caws/validate.ts +0 -228
  211. package/templates/apps/tools/caws/waivers.js +0 -344
  212. /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/waivers.schema.json +0 -0
  213. /package/{templates/apps/tools/caws → dist/templates/.caws}/schemas/working-spec.schema.json +0 -0
  214. /package/{templates/apps/tools/caws → dist/templates/.caws}/templates/working-spec.template.yml +0 -0
  215. /package/{templates/apps/tools/caws → dist/templates/.caws/tools}/scope-guard.js +0 -0
  216. /package/{templates/apps/tools/caws → dist/templates/.caws}/tools-allow.json +0 -0
  217. /package/{templates/apps/tools/caws → dist/templates/.caws}/waivers.yml +0 -0
@@ -1,391 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- /**
4
- * Spec-to-Test Mapper
5
- *
6
- * Links acceptance criteria from working-spec.yaml to actual test cases
7
- * for full traceability and coverage reporting.
8
- *
9
- * @author @darianrosebrook
10
- */
11
-
12
- import * as fs from 'fs';
13
- import * as path from 'path';
14
- import * as yaml from 'js-yaml';
15
-
16
- interface AcceptanceCriterion {
17
- id: string;
18
- given: string;
19
- when: string;
20
- then: string;
21
- test_file?: string;
22
- test_name?: string;
23
- type?: 'unit' | 'integration' | 'e2e' | 'property-based';
24
- }
25
-
26
- interface WorkingSpec {
27
- id: string;
28
- title: string;
29
- acceptance: AcceptanceCriterion[];
30
- }
31
-
32
- interface TestMapping {
33
- criterionId: string;
34
- criterion: AcceptanceCriterion;
35
- tests: TestReference[];
36
- covered: boolean;
37
- }
38
-
39
- interface TestReference {
40
- file: string;
41
- testName: string;
42
- lineNumber?: number;
43
- type: 'unit' | 'integration' | 'e2e' | 'property-based';
44
- }
45
-
46
- interface CoverageReport {
47
- totalCriteria: number;
48
- coveredCriteria: number;
49
- coveragePercentage: number;
50
- mappings: TestMapping[];
51
- uncoveredCriteria: string[];
52
- }
53
-
54
- export class SpecTestMapper {
55
- private workingSpecPath: string;
56
- private testDir: string;
57
-
58
- constructor(workingSpecPath: string = '.caws/working-spec.yaml', testDir: string = 'tests') {
59
- this.workingSpecPath = workingSpecPath;
60
- this.testDir = testDir;
61
- }
62
-
63
- /**
64
- * Load working spec and parse acceptance criteria
65
- */
66
- private loadWorkingSpec(): WorkingSpec {
67
- if (!fs.existsSync(this.workingSpecPath)) {
68
- throw new Error(`Working spec not found: ${this.workingSpecPath}`);
69
- }
70
-
71
- const content = fs.readFileSync(this.workingSpecPath, 'utf-8');
72
- const spec = yaml.load(content) as WorkingSpec;
73
-
74
- if (!spec.acceptance || spec.acceptance.length === 0) {
75
- throw new Error('No acceptance criteria found in working spec');
76
- }
77
-
78
- return spec;
79
- }
80
-
81
- /**
82
- * Find all test files
83
- */
84
- private findTestFiles(): string[] {
85
- const files: string[] = [];
86
-
87
- const walkDir = (dir: string): void => {
88
- if (!fs.existsSync(dir)) return;
89
-
90
- const items = fs.readdirSync(dir, { withFileTypes: true });
91
-
92
- for (const item of items) {
93
- const fullPath = path.join(dir, item.name);
94
-
95
- if (item.isDirectory() && item.name !== 'node_modules') {
96
- walkDir(fullPath);
97
- } else if (
98
- item.isFile() &&
99
- (item.name.endsWith('.test.ts') ||
100
- item.name.endsWith('.test.js') ||
101
- item.name.endsWith('.spec.ts') ||
102
- item.name.endsWith('.spec.js'))
103
- ) {
104
- files.push(fullPath);
105
- }
106
- }
107
- };
108
-
109
- walkDir(this.testDir);
110
- return files;
111
- }
112
-
113
- /**
114
- * Search for tests related to an acceptance criterion
115
- */
116
- private findRelatedTests(criterion: AcceptanceCriterion): TestReference[] {
117
- const testFiles = this.findTestFiles();
118
- const tests: TestReference[] = [];
119
-
120
- // Build search terms from the criterion
121
- const searchTerms = [
122
- criterion.id,
123
- ...this.extractKeywords(criterion.given),
124
- ...this.extractKeywords(criterion.when),
125
- ...this.extractKeywords(criterion.then),
126
- ];
127
-
128
- for (const file of testFiles) {
129
- const content = fs.readFileSync(file, 'utf-8');
130
-
131
- // Check if criterion ID is explicitly referenced
132
- if (content.includes(criterion.id) || content.includes(`[${criterion.id}]`)) {
133
- const testName = this.extractTestName(content, criterion.id);
134
- tests.push({
135
- file: path.relative(process.cwd(), file),
136
- testName: testName || `Tests for ${criterion.id}`,
137
- type: this.classifyTest(file),
138
- });
139
- continue;
140
- }
141
-
142
- // Check for keyword matches
143
- const keywordMatches = searchTerms.filter((term) =>
144
- content.toLowerCase().includes(term.toLowerCase())
145
- );
146
-
147
- if (keywordMatches.length >= 3) {
148
- // Threshold: at least 3 keyword matches
149
- const testName = this.extractTestName(content) || this.extractDescribeName(content);
150
- if (testName) {
151
- tests.push({
152
- file: path.relative(process.cwd(), file),
153
- testName,
154
- type: this.classifyTest(file),
155
- });
156
- }
157
- }
158
- }
159
-
160
- return tests;
161
- }
162
-
163
- /**
164
- * Extract keywords from a text string
165
- */
166
- private extractKeywords(text: string): string[] {
167
- return text
168
- .toLowerCase()
169
- .replace(/[^\w\s]/g, ' ')
170
- .split(/\s+/)
171
- .filter((word) => word.length > 4) // Only meaningful words
172
- .filter((word) => !['given', 'when', 'then', 'should', 'with'].includes(word));
173
- }
174
-
175
- /**
176
- * Extract test name from content
177
- */
178
- private extractTestName(content: string, criterionId?: string): string | null {
179
- const lines = content.split('\n');
180
-
181
- for (const line of lines) {
182
- if (criterionId && line.includes(criterionId)) {
183
- const match = line.match(/it\s*\(\s*["'](.+?)["']/);
184
- if (match) return match[1];
185
- }
186
-
187
- const itMatch = line.match(/it\s*\(\s*["'](.+?)["']/);
188
- if (itMatch) return itMatch[1];
189
- }
190
-
191
- return null;
192
- }
193
-
194
- /**
195
- * Extract describe block name
196
- */
197
- private extractDescribeName(content: string): string | null {
198
- const match = content.match(/describe\s*\(\s*["'](.+?)["']/);
199
- return match ? match[1] : null;
200
- }
201
-
202
- /**
203
- * Classify test type based on file path
204
- */
205
- private classifyTest(filePath: string): 'unit' | 'integration' | 'e2e' | 'property-based' {
206
- if (filePath.includes('e2e')) return 'e2e';
207
- if (filePath.includes('integration')) return 'integration';
208
- if (filePath.includes('property')) return 'property-based';
209
- return 'unit';
210
- }
211
-
212
- /**
213
- * Generate coverage report
214
- */
215
- async generateCoverageReport(): Promise<CoverageReport> {
216
- const spec = this.loadWorkingSpec();
217
- const mappings: TestMapping[] = [];
218
-
219
- console.log(`\n🔍 Analyzing ${spec.acceptance.length} acceptance criteria...\n`);
220
-
221
- for (const criterion of spec.acceptance) {
222
- const tests = this.findRelatedTests(criterion);
223
- const covered = tests.length > 0;
224
-
225
- mappings.push({
226
- criterionId: criterion.id,
227
- criterion,
228
- tests,
229
- covered,
230
- });
231
-
232
- if (covered) {
233
- console.log(`✅ ${criterion.id}: ${tests.length} related test(s) found`);
234
- } else {
235
- console.log(`❌ ${criterion.id}: No related tests found`);
236
- }
237
- }
238
-
239
- const coveredCount = mappings.filter((m) => m.covered).length;
240
- const coveragePercentage = (coveredCount / spec.acceptance.length) * 100;
241
- const uncoveredCriteria = mappings.filter((m) => !m.covered).map((m) => m.criterionId);
242
-
243
- return {
244
- totalCriteria: spec.acceptance.length,
245
- coveredCriteria: coveredCount,
246
- coveragePercentage,
247
- mappings,
248
- uncoveredCriteria,
249
- };
250
- }
251
-
252
- /**
253
- * Generate markdown report
254
- */
255
- generateMarkdownReport(report: CoverageReport): string {
256
- let md = `# Acceptance Criteria Coverage Report\n\n`;
257
- md += `**Coverage**: ${report.coveredCriteria}/${
258
- report.totalCriteria
259
- } (${report.coveragePercentage.toFixed(1)}%)\n\n`;
260
-
261
- md += `## Coverage Summary\n\n`;
262
- md += `| Criterion ID | Status | Related Tests |\n`;
263
- md += `|--------------|--------|---------------|\n`;
264
-
265
- for (const mapping of report.mappings) {
266
- const status = mapping.covered ? '✅ Covered' : '❌ Not Covered';
267
- const testCount = mapping.tests.length;
268
- md += `| ${mapping.criterionId} | ${status} | ${testCount} |\n`;
269
- }
270
-
271
- md += `\n## Detailed Mappings\n\n`;
272
-
273
- for (const mapping of report.mappings) {
274
- md += `### ${mapping.criterionId}\n\n`;
275
- md += `**Given**: ${mapping.criterion.given}\n`;
276
- md += `**When**: ${mapping.criterion.when}\n`;
277
- md += `**Then**: ${mapping.criterion.then}\n\n`;
278
-
279
- if (mapping.tests.length > 0) {
280
- md += `**Related Tests**:\n\n`;
281
- for (const test of mapping.tests) {
282
- md += `- [${test.type}] \`${test.file}\`\n`;
283
- md += ` - ${test.testName}\n`;
284
- }
285
- } else {
286
- md += `⚠️ **No related tests found**\n\n`;
287
- md += `**Recommendation**: Add test for this acceptance criterion.\n`;
288
- }
289
-
290
- md += `\n---\n\n`;
291
- }
292
-
293
- if (report.uncoveredCriteria.length > 0) {
294
- md += `## Uncovered Criteria\n\n`;
295
- md += `The following acceptance criteria have no related tests:\n\n`;
296
- for (const id of report.uncoveredCriteria) {
297
- md += `- ${id}\n`;
298
- }
299
- }
300
-
301
- return md;
302
- }
303
-
304
- /**
305
- * Save report to file
306
- */
307
- async saveReport(outputPath: string): Promise<void> {
308
- const report = await this.generateCoverageReport();
309
- const markdown = this.generateMarkdownReport(report);
310
-
311
- fs.writeFileSync(outputPath, markdown, 'utf-8');
312
- console.log(`\n📄 Report saved to: ${outputPath}`);
313
- }
314
-
315
- /**
316
- * Display console report
317
- */
318
- async displayReport(): Promise<void> {
319
- const report = await this.generateCoverageReport();
320
-
321
- console.log(`\n${'═'.repeat(80)}`);
322
- console.log(` ACCEPTANCE CRITERIA COVERAGE REPORT`);
323
- console.log(`${'═'.repeat(80)}\n`);
324
-
325
- console.log(
326
- `📊 Coverage: ${report.coveredCriteria}/${
327
- report.totalCriteria
328
- } (${report.coveragePercentage.toFixed(1)}%)\n`
329
- );
330
-
331
- if (report.uncoveredCriteria.length > 0) {
332
- console.log(`❌ Uncovered Criteria (${report.uncoveredCriteria.length}):`);
333
- for (const id of report.uncoveredCriteria) {
334
- const mapping = report.mappings.find((m) => m.criterionId === id);
335
- if (mapping) {
336
- console.log(`\n ${id}:`);
337
- console.log(` Given: ${mapping.criterion.given}`);
338
- console.log(` When: ${mapping.criterion.when}`);
339
- console.log(` Then: ${mapping.criterion.then}`);
340
- }
341
- }
342
- console.log();
343
- }
344
-
345
- if (report.coveredCriteria > 0) {
346
- console.log(`✅ Covered Criteria (${report.coveredCriteria}):`);
347
- for (const mapping of report.mappings.filter((m) => m.covered)) {
348
- console.log(`\n ${mapping.criterionId}: ${mapping.tests.length} test(s)`);
349
- for (const test of mapping.tests) {
350
- console.log(` - [${test.type}] ${test.testName}`);
351
- console.log(` 📁 ${test.file}`);
352
- }
353
- }
354
- }
355
-
356
- console.log(`\n${'═'.repeat(80)}\n`);
357
- }
358
- }
359
-
360
- // CLI interface
361
- if (import.meta.url === `file://${process.argv[1]}`) {
362
- (async () => {
363
- const command = process.argv[2];
364
- const mapper = new SpecTestMapper();
365
-
366
- switch (command) {
367
- case 'report':
368
- await mapper.displayReport();
369
- break;
370
-
371
- case 'save':
372
- const outputPath = process.argv[3] || '.caws/spec-coverage-report.md';
373
- await mapper.saveReport(outputPath);
374
- break;
375
-
376
- default:
377
- console.log(`
378
- Usage: spec-test-mapper <command> [options]
379
-
380
- Commands:
381
- report Display coverage report in console
382
- save [output-path] Save coverage report to file (default: .caws/spec-coverage-report.md)
383
-
384
- Examples:
385
- npx tsx apps/tools/caws/spec-test-mapper.ts report
386
- npx tsx apps/tools/caws/spec-test-mapper.ts save docs/spec-coverage.md
387
- `);
388
- process.exit(1);
389
- }
390
- })();
391
- }