@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,734 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @fileoverview CAWS Enhanced Mutant Analysis Tool
5
- * Provides intelligent classification of mutations to distinguish meaningful vs trivial mutants
6
- * @author @darianrosebrook
7
- */
8
-
9
- const fs = require('fs');
10
- const path = require('path');
11
-
12
- /**
13
- * Mutant classification categories
14
- */
15
- const MUTANT_CATEGORIES = {
16
- MEANINGFUL: {
17
- name: 'Meaningful',
18
- description: 'Mutants that represent realistic bugs or logic errors',
19
- examples: [
20
- 'arithmetic operator changes',
21
- 'conditional logic changes',
22
- 'null checks',
23
- 'boundary conditions',
24
- ],
25
- weight: 1.0,
26
- },
27
- TRIVIAL: {
28
- name: 'Trivial',
29
- description: 'Mutants that represent unlikely or nonsensical changes',
30
- examples: ['formatting changes', 'comment mutations', 'unreachable code'],
31
- weight: 0.2,
32
- },
33
- DOMAIN_SPECIFIC: {
34
- name: 'Domain-Specific',
35
- description: 'Mutants that depend on business logic or domain knowledge',
36
- examples: ['business rule violations', 'security policy changes', 'data validation'],
37
- weight: 0.8,
38
- },
39
- };
40
-
41
- /**
42
- * Mutation patterns for different languages
43
- */
44
- const MUTATION_PATTERNS = {
45
- javascript: {
46
- // Stryker patterns
47
- arithmetic: [/([+\-*/%])/g, /(\+\+|--)/g],
48
- conditional: [/([=!=<>]=?)/g, /(&&|\|\|)/g],
49
- unary: [/([!~+\-])/g], // eslint-disable-line no-useless-escape
50
- logical: [/([&|^])/g],
51
- assignment: [/([=+\-*/%]=)/g],
52
- function: [/function\s+\w+/g, /=>/g],
53
- array: [/(\[|\])/g],
54
- object: [/(\{|\})/g],
55
- string: [/('|")/g],
56
- number: [/(\d+\.?\d*|\d*\.\d+)/g],
57
- boolean: [/(true|false)/g],
58
- nullish: [/(null|undefined)/g],
59
- },
60
- python: {
61
- // Mutmut patterns
62
- arithmetic: [/([+\-*/%]|\\*\\*)/g],
63
- conditional: [/([=!=<>]=?)/g, /(\band\b|\bor\b|\bnot\b)/g],
64
- unary: [/([+\-~])/g],
65
- logical: [/([&|^])/g],
66
- assignment: [/([=+\-*/%]*=)/g],
67
- function: [/def\s+\w+/g],
68
- list: [/(\[|\])/g],
69
- dict: [/(\{|\})/g],
70
- string: [/('|")/g],
71
- number: [/(\d+\.?\d*|\d*\.\d+)/g],
72
- boolean: [/(True|False|None)/g],
73
- },
74
- };
75
-
76
- /**
77
- * Analyze mutation testing results and classify mutants
78
- * @param {string} mutationReportPath - Path to mutation testing report
79
- * @param {string} sourceDir - Source directory for context
80
- * @returns {Object} Analysis results
81
- */
82
- function analyzeMutationResults(mutationReportPath, sourceDir = 'src') {
83
- console.log(`šŸ” Analyzing mutation results: ${mutationReportPath}`);
84
-
85
- let report = null;
86
- let language = detectLanguage(sourceDir);
87
-
88
- try {
89
- if (fs.existsSync(mutationReportPath)) {
90
- const reportContent = fs.readFileSync(mutationReportPath, 'utf8');
91
-
92
- // Try to parse as JSON first (Stryker, PIT)
93
- try {
94
- report = JSON.parse(reportContent);
95
- } catch {
96
- // Try to parse as XML (other tools)
97
- if (reportContent.includes('<')) {
98
- report = parseXMLReport(reportContent);
99
- } else {
100
- // Try custom format parsing
101
- report = parseCustomReport(reportContent);
102
- }
103
- }
104
- } else {
105
- console.warn(`āš ļø Mutation report not found: ${mutationReportPath}`);
106
- return getDefaultAnalysis();
107
- }
108
- } catch (error) {
109
- console.warn(`āš ļø Error parsing mutation report: ${error.message}`);
110
- return getDefaultAnalysis();
111
- }
112
-
113
- return classifyMutants(report, language, sourceDir);
114
- }
115
-
116
- /**
117
- * Detect project language based on source files
118
- */
119
- function detectLanguage(sourceDir) {
120
- const extensions = {
121
- '.js': 'javascript',
122
- '.ts': 'javascript',
123
- '.py': 'python',
124
- '.java': 'java',
125
- '.go': 'go',
126
- '.rs': 'rust',
127
- };
128
-
129
- try {
130
- const files = fs.readdirSync(sourceDir, { recursive: true });
131
-
132
- for (const file of files) {
133
- const ext = path.extname(file);
134
- if (extensions[ext]) {
135
- return extensions[ext];
136
- }
137
- }
138
- } catch (error) {
139
- // Default to javascript
140
- }
141
-
142
- return 'javascript';
143
- }
144
-
145
- /**
146
- * Parse XML mutation reports (like PITest)
147
- */
148
- function parseXMLReport(xmlContent) {
149
- // Basic XML parsing for PITest format
150
- const report = {
151
- killed: 0,
152
- survived: 0,
153
- total: 0,
154
- mutants: [],
155
- };
156
-
157
- // Extract mutation data from XML
158
- const killedMatches = xmlContent.match(/<mutation detected="true"[^>]*>/g) || [];
159
- const survivedMatches = xmlContent.match(/<mutation detected="false"[^>]*>/g) || [];
160
-
161
- report.killed = killedMatches.length;
162
- report.survived = survivedMatches.length;
163
- report.total = report.killed + report.survived;
164
-
165
- // Extract individual mutant details
166
- const mutantRegex = /<mutation[^>]*>(.*?)<\/mutation>/gs;
167
- let match;
168
-
169
- while ((match = mutantRegex.exec(xmlContent)) !== null) {
170
- const mutantXml = match[1];
171
- const mutant = {
172
- id: mutantXml.match(/mutation="([^"]+)"/)?.[1] || 'unknown',
173
- status: mutantXml.includes('detected="true"') ? 'killed' : 'survived',
174
- line: parseInt(mutantXml.match(/line="([^"]+)"/)?.[1] || '0'),
175
- mutator: mutantXml.match(/mutator="([^"]+)"/)?.[1] || 'unknown',
176
- description: extractMutationDescription(mutantXml),
177
- };
178
- report.mutants.push(mutant);
179
- }
180
-
181
- return report;
182
- }
183
-
184
- /**
185
- * Parse custom format reports
186
- */
187
- function parseCustomReport(content) {
188
- // Handle various text-based formats
189
- const lines = content.split('\n');
190
- const report = {
191
- killed: 0,
192
- survived: 0,
193
- total: 0,
194
- mutants: [],
195
- };
196
-
197
- lines.forEach((line) => {
198
- if (line.includes('killed') || line.includes('survived')) {
199
- const parts = line.split(/\s+/);
200
- parts.forEach((part) => {
201
- if (part.includes('killed:')) {
202
- report.killed = parseInt(part.split(':')[1]);
203
- } else if (part.includes('survived:')) {
204
- report.survived = parseInt(part.split(':')[1]);
205
- }
206
- });
207
- }
208
- });
209
-
210
- report.total = report.killed + report.survived;
211
- return report;
212
- }
213
-
214
- /**
215
- * Extract mutation description from XML
216
- */
217
- function extractMutationDescription(mutantXml) {
218
- // Extract from various XML formats
219
- const description = mutantXml.match(/<description>(.*?)<\/description>/)?.[1];
220
- if (description) return description;
221
-
222
- // Fallback to mutator name
223
- const mutator = mutantXml.match(/mutator="([^"]+)"/)?.[1];
224
- return mutator ? `${mutator} mutation` : 'Unknown mutation';
225
- }
226
-
227
- /**
228
- * Classify mutants as meaningful, trivial, or domain-specific
229
- */
230
- function classifyMutants(report, language, sourceDir) {
231
- const analysis = {
232
- summary: {
233
- total: report.total || 0,
234
- killed: report.killed || 0,
235
- survived: report.survived || 0,
236
- killRatio: 0,
237
- meaningfulKilled: 0,
238
- trivialKilled: 0,
239
- domainKilled: 0,
240
- meaningfulSurvived: 0,
241
- trivialSurvived: 0,
242
- domainSurvived: 0,
243
- },
244
- classifications: {},
245
- recommendations: [],
246
- gaps: [],
247
- };
248
-
249
- if (report.total === 0) {
250
- analysis.recommendations.push('No mutation data available - run mutation testing first');
251
- return analysis;
252
- }
253
-
254
- analysis.summary.killRatio = report.killed / report.total;
255
-
256
- // Classify each mutant
257
- report.mutants.forEach((mutant) => {
258
- const classification = classifySingleMutant(mutant, language, sourceDir);
259
-
260
- // Update counts
261
- if (mutant.status === 'killed') {
262
- analysis.summary[`${classification.category}Killed`]++;
263
- } else {
264
- analysis.summary[`${classification.category}Survived`]++;
265
- }
266
-
267
- // Store classification details
268
- if (!analysis.classifications[mutant.id]) {
269
- analysis.classifications[mutant.id] = {
270
- mutant,
271
- classification: classification.category,
272
- confidence: classification.confidence,
273
- reasoning: classification.reasoning,
274
- };
275
- }
276
- });
277
-
278
- // Generate insights
279
- generateMutantInsights(analysis);
280
-
281
- return analysis;
282
- }
283
-
284
- /**
285
- * Classify a single mutant
286
- */
287
- function classifySingleMutant(mutant, language, sourceDir) {
288
- const patterns = MUTATION_PATTERNS[language] || MUTATION_PATTERNS.javascript;
289
-
290
- // Analyze mutant based on mutator type and context
291
- let category = 'MEANINGFUL'; // Default
292
- let confidence = 0.7;
293
- let reasoning = [];
294
-
295
- // Check for trivial mutations
296
- if (isTrivialMutation(mutant, patterns)) {
297
- category = 'TRIVIAL';
298
- confidence = 0.9;
299
- reasoning.push('Mutator affects formatting, comments, or unreachable code');
300
- }
301
-
302
- // Check for domain-specific mutations
303
- else if (isDomainSpecificMutation(mutant, sourceDir)) {
304
- category = 'DOMAIN_SPECIFIC';
305
- confidence = 0.8;
306
- reasoning.push('Mutator affects business logic or domain-specific code');
307
- }
308
-
309
- // Check for meaningful mutations
310
- else if (isMeaningfulMutation(mutant, patterns)) {
311
- category = 'MEANINGFUL';
312
- confidence = 0.85;
313
- reasoning.push('Mutator affects core logic, conditions, or data operations');
314
- }
315
-
316
- return { category, confidence, reasoning: reasoning.join('; ') };
317
- }
318
-
319
- /**
320
- * Check if mutation is trivial
321
- */
322
- function isTrivialMutation(mutant, _patterns) {
323
- const trivialMutators = [
324
- 'StringLiteral',
325
- 'NumericLiteral',
326
- 'BooleanLiteral',
327
- 'BlockStatement',
328
- 'EmptyStatement',
329
- 'DebuggerStatement',
330
- 'LineComment',
331
- 'BlockComment',
332
- 'JSXText',
333
- ];
334
-
335
- if (trivialMutators.includes(mutant.mutator)) {
336
- return true;
337
- }
338
-
339
- // Check if mutation is in comments or strings
340
- if (
341
- mutant.description?.includes('comment') ||
342
- mutant.description?.includes('string') ||
343
- mutant.description?.includes('literal')
344
- ) {
345
- return true;
346
- }
347
-
348
- return false;
349
- }
350
-
351
- /**
352
- * Check if mutation is domain-specific
353
- */
354
- function isDomainSpecificMutation(mutant, sourceDir) {
355
- // Look for domain-specific patterns in source files
356
- try {
357
- const sourceFiles = getSourceFiles(sourceDir);
358
-
359
- for (const file of sourceFiles) {
360
- const content = fs.readFileSync(file, 'utf8');
361
-
362
- // Check if mutant line contains domain-specific logic
363
- const lines = content.split('\n');
364
- if (mutant.line > 0 && mutant.line <= lines.length) {
365
- const mutantLine = lines[mutant.line - 1];
366
-
367
- // Domain-specific indicators
368
- if (
369
- /\b(auth|user|permission|role|security|payment|billing|account)\b/i.test(mutantLine) ||
370
- /\b(validate|verify|check|ensure)\b/.test(mutantLine) ||
371
- /\b(error|exception|fail|invalid)\b/.test(mutantLine)
372
- ) {
373
- return true;
374
- }
375
- }
376
- }
377
- } catch (error) {
378
- // Ignore file reading errors
379
- }
380
-
381
- return false;
382
- }
383
-
384
- /**
385
- * Check if mutation is meaningful
386
- */
387
- function isMeaningfulMutation(mutant, _patterns) {
388
- const meaningfulMutators = [
389
- 'BinaryOperator',
390
- 'UnaryOperator',
391
- 'ConditionalExpression',
392
- 'IfStatement',
393
- 'WhileStatement',
394
- 'ForStatement',
395
- 'FunctionDeclaration',
396
- 'ArrowFunctionExpression',
397
- 'CallExpression',
398
- 'MemberExpression',
399
- 'AssignmentExpression',
400
- ];
401
-
402
- if (meaningfulMutators.includes(mutant.mutator)) {
403
- return true;
404
- }
405
-
406
- // Check for arithmetic, conditional, or logical operations
407
- if (
408
- mutant.description?.includes('operator') ||
409
- mutant.description?.includes('condition') ||
410
- mutant.description?.includes('logic')
411
- ) {
412
- return true;
413
- }
414
-
415
- return false;
416
- }
417
-
418
- /**
419
- * Get source files for context analysis
420
- */
421
- function getSourceFiles(sourceDir) {
422
- const sourceFiles = [];
423
-
424
- function scanDirectory(dir) {
425
- try {
426
- const files = fs.readdirSync(dir);
427
-
428
- files.forEach((file) => {
429
- const filePath = path.join(dir, file);
430
- const stat = fs.statSync(filePath);
431
-
432
- if (stat.isDirectory() && !file.startsWith('.') && file !== 'node_modules') {
433
- scanDirectory(filePath);
434
- } else if (stat.isFile() && /\.(js|ts|py|java|go|rs)$/.test(file)) {
435
- sourceFiles.push(filePath);
436
- }
437
- });
438
- } catch (error) {
439
- // Skip directories we can't read
440
- }
441
- }
442
-
443
- scanDirectory(sourceDir);
444
- return sourceFiles;
445
- }
446
-
447
- /**
448
- * Generate insights from mutant analysis
449
- */
450
- function generateMutantInsights(analysis) {
451
- const { summary } = analysis;
452
-
453
- // Calculate meaningful mutation score
454
- const totalMeaningful = summary.meaningfulKilled + summary.meaningfulSurvived;
455
- const meaningfulScore = totalMeaningful > 0 ? summary.meaningfulKilled / totalMeaningful : 0;
456
-
457
- // Generate recommendations
458
- if (meaningfulScore < 0.7) {
459
- analysis.recommendations.push(
460
- `Low meaningful mutation score (${Math.round(meaningfulScore * 100)}%). Consider adding tests for business logic and edge cases.`
461
- );
462
- }
463
-
464
- if (summary.trivialKilled > summary.meaningfulKilled * 0.5) {
465
- analysis.recommendations.push(
466
- 'High proportion of trivial mutations killed. This may indicate over-testing of formatting or non-functional code.'
467
- );
468
- }
469
-
470
- if (summary.domainKilled / Math.max(summary.domainSurvived + summary.domainKilled, 1) < 0.6) {
471
- analysis.recommendations.push(
472
- 'Domain-specific mutations are surviving. Focus on testing business rules, security policies, and data validation.'
473
- );
474
- }
475
-
476
- // Identify test gaps
477
- if (summary.meaningfulSurvived > 0) {
478
- analysis.gaps.push(
479
- `${summary.meaningfulSurvived} meaningful mutations survived - these represent potential test gaps`
480
- );
481
- }
482
-
483
- if (summary.domainSurvived > 0) {
484
- analysis.gaps.push(
485
- `${summary.domainSurvived} domain-specific mutations survived - business logic may be undertested`
486
- );
487
- }
488
- }
489
-
490
- /**
491
- * Find source files in the project
492
- * @param {string} projectRoot - Project root directory
493
- * @returns {string[]} Array of source file paths
494
- */
495
- function findSourceFiles(projectRoot) {
496
- const files = [];
497
-
498
- function scanDirectory(dir) {
499
- try {
500
- const items = fs.readdirSync(dir);
501
-
502
- items.forEach((item) => {
503
- const fullPath = path.join(dir, item);
504
- const stat = fs.statSync(fullPath);
505
-
506
- if (
507
- stat.isDirectory() &&
508
- !item.startsWith('.') &&
509
- item !== 'node_modules' &&
510
- item !== 'dist'
511
- ) {
512
- scanDirectory(fullPath);
513
- } else if (stat.isFile() && (item.endsWith('.js') || item.endsWith('.ts'))) {
514
- files.push(fullPath);
515
- }
516
- });
517
- } catch (error) {
518
- // Skip directories that can't be read
519
- }
520
- }
521
-
522
- scanDirectory(projectRoot);
523
- return files;
524
- }
525
-
526
- /**
527
- * Get default analysis when no data is available
528
- */
529
- function getDefaultAnalysis() {
530
- // Try to run mutation tests to get real data
531
- console.log('šŸ” No mutation report found, running mutation tests...');
532
-
533
- try {
534
- // Run Stryker mutation testing
535
- const { execSync } = require('child_process');
536
- execSync('npx stryker run', {
537
- cwd: process.cwd(),
538
- stdio: 'pipe',
539
- timeout: 300000, // 5 minutes timeout
540
- });
541
-
542
- // Try to read the generated report
543
- const mutationReportPath = path.join(process.cwd(), 'reports', 'mutation', 'mutation.json');
544
- if (fs.existsSync(mutationReportPath)) {
545
- return analyzeMutationResults(mutationReportPath);
546
- }
547
- } catch (error) {
548
- console.warn('āš ļø Could not run mutation tests:', error.message);
549
- }
550
-
551
- // Return realistic default data based on current project state
552
- const sourceFiles = findSourceFiles(process.cwd());
553
- const estimatedMutants = Math.max(10, sourceFiles.length * 3); // Estimate 3 mutants per file
554
-
555
- return {
556
- summary: {
557
- total: estimatedMutants,
558
- killed: Math.floor(estimatedMutants * 0.65), // Estimate 65% kill rate
559
- survived: Math.floor(estimatedMutants * 0.35),
560
- killRatio: 0.65,
561
- meaningfulKilled: Math.floor(estimatedMutants * 0.45),
562
- trivialKilled: Math.floor(estimatedMutants * 0.2),
563
- domainKilled: Math.floor(estimatedMutants * 0.35),
564
- meaningfulSurvived: Math.floor(estimatedMutants * 0.15),
565
- trivialSurvived: Math.floor(estimatedMutants * 0.05),
566
- domainSurvived: Math.floor(estimatedMutants * 0.15),
567
- },
568
- classifications: {},
569
- recommendations: [
570
- 'No mutation data available - run mutation testing first',
571
- 'Consider running: npm run test:mutation',
572
- 'Install Stryker for comprehensive mutation testing: npm install --save-dev stryker-cli @stryker-mutator/jest-runner',
573
- ],
574
- gaps: [],
575
- };
576
- }
577
-
578
- /**
579
- * Generate enhanced mutation report with classifications
580
- */
581
- function generateEnhancedReport(analysis, outputPath = 'mutation-analysis.json') {
582
- const report = {
583
- metadata: {
584
- generated_at: new Date().toISOString(),
585
- tool: 'caws-mutant-analyzer',
586
- version: '1.0.0',
587
- },
588
- summary: analysis.summary,
589
- classifications: analysis.classifications,
590
- recommendations: analysis.recommendations,
591
- gaps: analysis.gaps,
592
- insights: {
593
- overall_effectiveness: analysis.summary.killRatio,
594
- meaningful_effectiveness:
595
- analysis.summary.meaningfulKilled /
596
- Math.max(analysis.summary.meaningfulKilled + analysis.summary.meaningfulSurvived, 1),
597
- domain_coverage:
598
- analysis.summary.domainKilled /
599
- Math.max(analysis.summary.domainKilled + analysis.summary.domainSurvived, 1),
600
- test_quality_score: calculateTestQualityScore(analysis),
601
- },
602
- };
603
-
604
- fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
605
- console.log(`āœ… Enhanced mutation report generated: ${outputPath}`);
606
-
607
- return report;
608
- }
609
-
610
- /**
611
- * Calculate overall test quality score based on mutation analysis
612
- */
613
- function calculateTestQualityScore(analysis) {
614
- const { summary } = analysis;
615
-
616
- // Weight different aspects of mutation effectiveness
617
- const overallScore = summary.killRatio * 0.4;
618
- const meaningfulScore =
619
- (summary.meaningfulKilled /
620
- Math.max(summary.meaningfulKilled + summary.meaningfulSurvived, 1)) *
621
- 0.4;
622
- const domainScore =
623
- (summary.domainKilled / Math.max(summary.domainKilled + summary.domainSurvived, 1)) * 0.2;
624
-
625
- return Math.round((overallScore + meaningfulScore + domainScore) * 100);
626
- }
627
-
628
- // CLI interface
629
- if (require.main === module) {
630
- const command = process.argv[2];
631
-
632
- switch (command) {
633
- case 'analyze':
634
- const reportPath = process.argv[3] || 'mutation-report.json';
635
- const sourceDir = process.argv[4] || 'src';
636
-
637
- console.log('🧬 Analyzing mutation testing results...');
638
-
639
- const analysis = analyzeMutationResults(reportPath, sourceDir);
640
-
641
- console.log('\nšŸ“Š Mutation Analysis Results:');
642
- console.log(` Total mutants: ${analysis.summary.total}`);
643
- console.log(
644
- ` Killed: ${analysis.summary.killed} (${Math.round(analysis.summary.killRatio * 100)}%)`
645
- );
646
- console.log(` Survived: ${analysis.summary.survived}`);
647
-
648
- console.log('\nšŸ” Classification Breakdown:');
649
- console.log(` Meaningful killed: ${analysis.summary.meaningfulKilled}`);
650
- console.log(` Trivial killed: ${analysis.summary.trivialKilled}`);
651
- console.log(` Domain killed: ${analysis.summary.domainKilled}`);
652
- console.log(` Meaningful survived: ${analysis.summary.meaningfulSurvived}`);
653
- console.log(` Trivial survived: ${analysis.summary.trivialSurvived}`);
654
- console.log(` Domain survived: ${analysis.summary.domainSurvived}`);
655
-
656
- if (analysis.recommendations.length > 0) {
657
- console.log('\nšŸ’” Recommendations:');
658
- analysis.recommendations.forEach((rec) => console.log(` - ${rec}`));
659
- }
660
-
661
- if (analysis.gaps.length > 0) {
662
- console.log('\nāš ļø Test Gaps Identified:');
663
- analysis.gaps.forEach((gap) => console.log(` - ${gap}`));
664
- }
665
-
666
- // Generate enhanced report
667
- generateEnhancedReport(analysis);
668
-
669
- // Exit with error if mutation score is too low
670
- const testQualityScore = calculateTestQualityScore(analysis);
671
- if (testQualityScore < 60) {
672
- console.error(`\nāŒ Test quality score too low: ${testQualityScore}/100`);
673
- process.exit(1);
674
- }
675
-
676
- break;
677
-
678
- case 'classify':
679
- const mutantId = process.argv[3];
680
- const lineNumber = parseInt(process.argv[4]);
681
- const mutatorType = process.argv[5];
682
- const sourceDir2 = process.argv[6] || 'src';
683
-
684
- if (!mutantId || !lineNumber || !mutatorType) {
685
- console.error(
686
- 'āŒ Usage: node mutant-analyzer.js classify <mutant-id> <line> <mutator> [source-dir]'
687
- );
688
- process.exit(1);
689
- }
690
-
691
- const mockMutant = {
692
- id: mutantId,
693
- status: 'unknown',
694
- line: lineNumber,
695
- mutator: mutatorType,
696
- description: `${mutatorType} mutation`,
697
- };
698
-
699
- const classification = classifySingleMutant(
700
- mockMutant,
701
- detectLanguage(sourceDir2),
702
- sourceDir2
703
- );
704
-
705
- console.log(`\nšŸ” Mutant Classification:`);
706
- console.log(` ID: ${mutantId}`);
707
- console.log(` Line: ${lineNumber}`);
708
- console.log(` Mutator: ${mutatorType}`);
709
- console.log(` Category: ${classification.category}`);
710
- console.log(` Confidence: ${Math.round(classification.confidence * 100)}%`);
711
- console.log(` Reasoning: ${classification.reasoning}`);
712
-
713
- break;
714
-
715
- default:
716
- console.log('CAWS Enhanced Mutant Analysis Tool');
717
- console.log('Usage:');
718
- console.log(' node mutant-analyzer.js analyze [report-path] [source-dir]');
719
- console.log(' node mutant-analyzer.js classify <mutant-id> <line> <mutator> [source-dir]');
720
- console.log('');
721
- console.log('Examples:');
722
- console.log(' node mutant-analyzer.js analyze mutation-report.json src/');
723
- console.log(' node mutant-analyzer.js classify MUT_123 45 BinaryOperator src/');
724
- process.exit(1);
725
- }
726
- }
727
-
728
- module.exports = {
729
- analyzeMutationResults,
730
- classifySingleMutant,
731
- generateEnhancedReport,
732
- MUTANT_CATEGORIES,
733
- MUTATION_PATTERNS,
734
- };