@paths.design/caws-cli 2.0.0 → 3.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 (50) hide show
  1. package/dist/index.d.ts.map +1 -1
  2. package/dist/index.js +101 -96
  3. package/package.json +3 -3
  4. package/templates/agents.md +820 -0
  5. package/templates/apps/tools/caws/COMPLETION_REPORT.md +331 -0
  6. package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +360 -0
  7. package/templates/apps/tools/caws/README.md +463 -0
  8. package/templates/apps/tools/caws/TEST_STATUS.md +365 -0
  9. package/templates/apps/tools/caws/attest.js +357 -0
  10. package/templates/apps/tools/caws/ci-optimizer.js +642 -0
  11. package/templates/apps/tools/caws/config.ts +245 -0
  12. package/templates/apps/tools/caws/cross-functional.js +876 -0
  13. package/templates/apps/tools/caws/dashboard.js +1112 -0
  14. package/templates/apps/tools/caws/flake-detector.ts +362 -0
  15. package/templates/apps/tools/caws/gates.js +198 -0
  16. package/templates/apps/tools/caws/gates.ts +237 -0
  17. package/templates/apps/tools/caws/language-adapters.ts +381 -0
  18. package/templates/apps/tools/caws/language-support.d.ts +367 -0
  19. package/templates/apps/tools/caws/language-support.d.ts.map +1 -0
  20. package/templates/apps/tools/caws/language-support.js +585 -0
  21. package/templates/apps/tools/caws/legacy-assessment.ts +408 -0
  22. package/templates/apps/tools/caws/legacy-assessor.js +764 -0
  23. package/templates/apps/tools/caws/mutant-analyzer.js +734 -0
  24. package/templates/apps/tools/caws/perf-budgets.ts +349 -0
  25. package/templates/apps/tools/caws/property-testing.js +707 -0
  26. package/templates/apps/tools/caws/provenance.d.ts +14 -0
  27. package/templates/apps/tools/caws/provenance.d.ts.map +1 -0
  28. package/templates/apps/tools/caws/provenance.js +132 -0
  29. package/templates/apps/tools/caws/provenance.ts +211 -0
  30. package/templates/apps/tools/caws/schemas/waivers.schema.json +30 -0
  31. package/templates/apps/tools/caws/schemas/working-spec.schema.json +115 -0
  32. package/templates/apps/tools/caws/scope-guard.js +208 -0
  33. package/templates/apps/tools/caws/security-provenance.ts +483 -0
  34. package/templates/apps/tools/caws/shared/base-tool.ts +281 -0
  35. package/templates/apps/tools/caws/shared/config-manager.ts +366 -0
  36. package/templates/apps/tools/caws/shared/gate-checker.ts +597 -0
  37. package/templates/apps/tools/caws/shared/types.ts +444 -0
  38. package/templates/apps/tools/caws/shared/validator.ts +305 -0
  39. package/templates/apps/tools/caws/shared/waivers-manager.ts +174 -0
  40. package/templates/apps/tools/caws/spec-test-mapper.ts +391 -0
  41. package/templates/apps/tools/caws/templates/working-spec.template.yml +60 -0
  42. package/templates/apps/tools/caws/test-quality.js +578 -0
  43. package/templates/apps/tools/caws/tools-allow.json +331 -0
  44. package/templates/apps/tools/caws/validate.js +76 -0
  45. package/templates/apps/tools/caws/validate.ts +228 -0
  46. package/templates/apps/tools/caws/waivers.js +344 -0
  47. package/templates/apps/tools/caws/waivers.yml +19 -0
  48. package/templates/codemod/README.md +1 -0
  49. package/templates/codemod/test.js +1 -0
  50. package/templates/docs/README.md +150 -0
@@ -0,0 +1,362 @@
1
+ #!/usr/bin/env tsx
2
+
3
+ /**
4
+ * CAWS Flake Detection System
5
+ *
6
+ * Monitors test variance and quarantines intermittently failing tests.
7
+ * This tool analyzes test run variance and identifies flaky tests for quarantine.
8
+ *
9
+ * @author @darianrosebrook
10
+ */
11
+
12
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
13
+ import { dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ interface TestResult {
17
+ title: string;
18
+ fullName: string;
19
+ status: 'passed' | 'failed' | 'pending' | 'skipped';
20
+ duration: number;
21
+ failureMessages: string[];
22
+ }
23
+
24
+ interface TestSuiteResult {
25
+ name: string;
26
+ status: 'passed' | 'failed';
27
+ testResults: TestResult[];
28
+ startTime: number;
29
+ endTime: number;
30
+ }
31
+
32
+ interface FlakeDetectionResult {
33
+ flakyTests: string[];
34
+ varianceScore: number;
35
+ totalRuns: number;
36
+ recommendations: string[];
37
+ }
38
+
39
+ interface HistoricalTestData {
40
+ runs: TestRun[];
41
+ quarantined: Set<string>;
42
+ lastUpdated: string;
43
+ }
44
+
45
+ interface TestRun {
46
+ timestamp: number;
47
+ results: Map<string, TestResult>;
48
+ variance: number;
49
+ }
50
+
51
+ /**
52
+ * Flake Detection Service
53
+ * Analyzes test run variance and identifies flaky tests
54
+ */
55
+ class FlakeDetectionService {
56
+ private readonly HISTORY_FILE = '.caws/flake-history.json';
57
+ private readonly QUARANTINE_FILE = '.caws/quarantined-tests.json';
58
+ private readonly VARIANCE_THRESHOLD = 0.05; // 5% variance threshold
59
+ private readonly MIN_RUNS_FOR_ANALYSIS = 3;
60
+ private readonly QUARANTINE_THRESHOLD = 0.15; // 15% flake rate triggers quarantine
61
+
62
+ /**
63
+ * Analyze test variance and detect flaky tests
64
+ */
65
+ async detectFlakes(currentResults: TestSuiteResult[]): Promise<FlakeDetectionResult> {
66
+ const history = this.loadHistory();
67
+ const currentRun = this.createCurrentRun(currentResults);
68
+
69
+ history.runs.push(currentRun);
70
+ this.saveHistory(history);
71
+
72
+ if (history.runs.length < this.MIN_RUNS_FOR_ANALYSIS) {
73
+ return {
74
+ flakyTests: [],
75
+ varianceScore: 0,
76
+ totalRuns: history.runs.length,
77
+ recommendations: [
78
+ `Need ${this.MIN_RUNS_FOR_ANALYSIS - history.runs.length} more test runs for analysis`,
79
+ ],
80
+ };
81
+ }
82
+
83
+ const flakyTests = this.identifyFlakyTests(history);
84
+ const varianceScore = this.calculateVarianceScore(history);
85
+
86
+ const recommendations = this.generateRecommendations(flakyTests, varianceScore);
87
+
88
+ return {
89
+ flakyTests,
90
+ varianceScore,
91
+ totalRuns: history.runs.length,
92
+ recommendations,
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Quarantine flaky tests
98
+ */
99
+ quarantineTests(testNames: string[]): void {
100
+ const history = this.loadHistory();
101
+ testNames.forEach((testName) => history.quarantined.add(testName));
102
+ history.lastUpdated = new Date().toISOString();
103
+ this.saveHistory(history);
104
+
105
+ // Save quarantined tests list
106
+ const quarantinedData = {
107
+ quarantined: Array.from(history.quarantined),
108
+ quarantinedAt: history.lastUpdated,
109
+ reason: 'Automated flake detection',
110
+ };
111
+
112
+ writeFileSync(this.QUARANTINE_FILE, JSON.stringify(quarantinedData, null, 2));
113
+ console.log(`🚫 Quarantined ${testNames.length} flaky tests`);
114
+ }
115
+
116
+ /**
117
+ * Get currently quarantined tests
118
+ */
119
+ getQuarantinedTests(): string[] {
120
+ const history = this.loadHistory();
121
+ return Array.from(history.quarantined);
122
+ }
123
+
124
+ /**
125
+ * Release tests from quarantine (manual override)
126
+ */
127
+ releaseFromQuarantine(testNames: string[]): void {
128
+ const history = this.loadHistory();
129
+ testNames.forEach((testName) => history.quarantined.delete(testName));
130
+ history.lastUpdated = new Date().toISOString();
131
+ this.saveHistory(history);
132
+ console.log(`✅ Released ${testNames.length} tests from quarantine`);
133
+ }
134
+
135
+ private loadHistory(): HistoricalTestData {
136
+ if (!existsSync(this.HISTORY_FILE)) {
137
+ return {
138
+ runs: [],
139
+ quarantined: new Set(),
140
+ lastUpdated: new Date().toISOString(),
141
+ };
142
+ }
143
+
144
+ try {
145
+ const data = JSON.parse(readFileSync(this.HISTORY_FILE, 'utf-8'));
146
+ return {
147
+ runs: data.runs || [],
148
+ quarantined: new Set(data.quarantined || []),
149
+ lastUpdated: data.lastUpdated || new Date().toISOString(),
150
+ };
151
+ } catch {
152
+ return {
153
+ runs: [],
154
+ quarantined: new Set(),
155
+ lastUpdated: new Date().toISOString(),
156
+ };
157
+ }
158
+ }
159
+
160
+ private saveHistory(history: HistoricalTestData): void {
161
+ const data = {
162
+ runs: history.runs,
163
+ quarantined: Array.from(history.quarantined),
164
+ lastUpdated: history.lastUpdated,
165
+ };
166
+ writeFileSync(this.HISTORY_FILE, JSON.stringify(data, null, 2));
167
+ }
168
+
169
+ private createCurrentRun(results: TestSuiteResult[]): TestRun {
170
+ const testMap = new Map<string, TestResult>();
171
+
172
+ results.forEach((suite) => {
173
+ suite.testResults.forEach((test) => {
174
+ const key = this.getTestKey(test);
175
+ testMap.set(key, test);
176
+ });
177
+ });
178
+
179
+ const variance = this.calculateRunVariance(testMap, results);
180
+ return {
181
+ timestamp: Date.now(),
182
+ results: testMap,
183
+ variance,
184
+ };
185
+ }
186
+
187
+ private getTestKey(test: TestResult): string {
188
+ return test.fullName;
189
+ }
190
+
191
+ private identifyFlakyTests(history: HistoricalTestData): string[] {
192
+ const flakyTests = new Set<string>();
193
+ const recentRuns = history.runs.slice(-5); // Analyze last 5 runs
194
+
195
+ // Find tests that have inconsistent results
196
+ for (const run of recentRuns) {
197
+ for (const [testName, result] of run.results) {
198
+ if (result.status !== 'passed') {
199
+ // Check if this test has passed in other recent runs
200
+ const passedInOtherRuns = recentRuns
201
+ .filter((r) => r !== run)
202
+ .some((r) => r.results.get(testName)?.status === 'passed');
203
+
204
+ if (passedInOtherRuns) {
205
+ flakyTests.add(testName);
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ // Check against quarantine threshold
212
+ for (const testName of flakyTests) {
213
+ const flakeRate = this.calculateFlakeRate(testName, recentRuns);
214
+ if (flakeRate < this.QUARANTINE_THRESHOLD) {
215
+ flakyTests.delete(testName);
216
+ }
217
+ }
218
+
219
+ return Array.from(flakyTests);
220
+ }
221
+
222
+ private calculateFlakeRate(testName: string, runs: TestRun[]): number {
223
+ const results = runs.map((run) => run.results.get(testName)?.status).filter(Boolean);
224
+ const failures = results.filter((status) => status !== 'passed').length;
225
+ return failures / results.length;
226
+ }
227
+
228
+ private calculateVarianceScore(history: HistoricalTestData): number {
229
+ if (history.runs.length < 2) return 0;
230
+
231
+ const recentRuns = history.runs.slice(-5);
232
+ const varianceSum = recentRuns.reduce((sum, run) => sum + run.variance, 0);
233
+ return varianceSum / recentRuns.length;
234
+ }
235
+
236
+ private calculateRunVariance(
237
+ testMap: Map<string, TestResult>,
238
+ _suites: TestSuiteResult[]
239
+ ): number {
240
+ const totalTests = testMap.size;
241
+ const failedTests = Array.from(testMap.values()).filter((t) => t.status !== 'passed').length;
242
+ return totalTests > 0 ? failedTests / totalTests : 0;
243
+ }
244
+
245
+ private generateRecommendations(flakyTests: string[], varianceScore: number): string[] {
246
+ const recommendations: string[] = [];
247
+
248
+ if (flakyTests.length > 0) {
249
+ recommendations.push(`Quarantine ${flakyTests.length} flaky tests for investigation`);
250
+ }
251
+
252
+ if (varianceScore > this.VARIANCE_THRESHOLD) {
253
+ recommendations.push('High test variance detected - consider test environment stability');
254
+ }
255
+
256
+ if (varianceScore === 0) {
257
+ recommendations.push('Excellent test stability - no flakes detected');
258
+ }
259
+
260
+ return recommendations;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * CLI Interface
266
+ */
267
+ async function main() {
268
+ const args = process.argv.slice(2);
269
+
270
+ if (args.length === 0) {
271
+ console.log('🔍 CAWS Flake Detection Tool');
272
+ console.log('Usage: flake-detector.ts <command> [options]');
273
+ console.log('');
274
+ console.log('Commands:');
275
+ console.log(' detect - Analyze test variance and detect flaky tests');
276
+ console.log(' quarantine - Quarantine specified flaky tests');
277
+ console.log(' release - Release tests from quarantine');
278
+ console.log(' status - Show current flake detection status');
279
+ console.log('');
280
+ console.log('Examples:');
281
+ console.log(' flake-detector.ts detect');
282
+ console.log(' flake-detector.ts quarantine "test name"');
283
+ console.log(' flake-detector.ts release "test name"');
284
+ return;
285
+ }
286
+
287
+ const command = args[0];
288
+ const detector = new FlakeDetectionService();
289
+
290
+ try {
291
+ switch (command) {
292
+ case 'detect': {
293
+ console.log('🔍 Analyzing test variance...');
294
+ // In a real implementation, you'd read test results from files
295
+ // For now, we'll simulate with mock data
296
+ const mockResults: TestSuiteResult[] = [];
297
+ const result = await detector.detectFlakes(mockResults);
298
+
299
+ console.log(`📊 Flake Detection Results:`);
300
+ console.log(` Variance Score: ${(result.varianceScore * 100).toFixed(2)}%`);
301
+ console.log(` Total Runs Analyzed: ${result.totalRuns}`);
302
+ console.log(` Flaky Tests Found: ${result.flakyTests.length}`);
303
+
304
+ if (result.flakyTests.length > 0) {
305
+ console.log('\n🚨 Flaky Tests:');
306
+ result.flakyTests.forEach((test) => console.log(` - ${test}`));
307
+ }
308
+
309
+ result.recommendations.forEach((rec) => console.log(`💡 ${rec}`));
310
+ break;
311
+ }
312
+
313
+ case 'quarantine': {
314
+ const testNames = args.slice(1);
315
+ if (testNames.length === 0) {
316
+ console.log('❌ Please specify test names to quarantine');
317
+ return;
318
+ }
319
+ detector.quarantineTests(testNames);
320
+ break;
321
+ }
322
+
323
+ case 'release': {
324
+ const testNames = args.slice(1);
325
+ if (testNames.length === 0) {
326
+ console.log('❌ Please specify test names to release');
327
+ return;
328
+ }
329
+ detector.releaseFromQuarantine(testNames);
330
+ break;
331
+ }
332
+
333
+ case 'status': {
334
+ const quarantined = detector.getQuarantinedTests();
335
+ console.log('🚫 Currently Quarantined Tests:');
336
+ if (quarantined.length === 0) {
337
+ console.log(' None - all tests are active');
338
+ } else {
339
+ quarantined.forEach((test) => console.log(` - ${test}`));
340
+ }
341
+ break;
342
+ }
343
+
344
+ default:
345
+ console.log(`❌ Unknown command: ${command}`);
346
+ process.exit(1);
347
+ }
348
+ } catch (error) {
349
+ console.error('❌ Error:', error);
350
+ process.exit(1);
351
+ }
352
+ }
353
+
354
+ // Run CLI if this file is executed directly
355
+ const __filename = fileURLToPath(import.meta.url);
356
+ const __dirname = dirname(__filename);
357
+
358
+ if (import.meta.url === `file://${process.argv[1]}`) {
359
+ main().catch(console.error);
360
+ }
361
+
362
+ export { FlakeDetectionService };
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @fileoverview CAWS Gates Tool - Enhanced Implementation
5
+ * @author @darianrosebrook
6
+ *
7
+ * Note: For enhanced TypeScript version with full gate checking, use gates.ts
8
+ * This .js version provides basic gate enforcement for backward compatibility
9
+ */
10
+
11
+ // Tier policies for quality gates
12
+ const TIER_POLICIES = {
13
+ 1: {
14
+ branch_coverage: 0.9,
15
+ mutation_score: 0.7,
16
+ max_files: 40,
17
+ max_loc: 1500,
18
+ trust_score: 85,
19
+ },
20
+ 2: {
21
+ branch_coverage: 0.8,
22
+ mutation_score: 0.5,
23
+ max_files: 25,
24
+ max_loc: 1000,
25
+ trust_score: 82,
26
+ },
27
+ 3: {
28
+ branch_coverage: 0.7,
29
+ mutation_score: 0.3,
30
+ max_files: 15,
31
+ max_loc: 500,
32
+ trust_score: 75,
33
+ },
34
+ };
35
+
36
+ /**
37
+ * Show tier policy
38
+ * @param {number} tier - Risk tier (1-3)
39
+ */
40
+ function showTierPolicy(tier = 1) {
41
+ const policy = TIER_POLICIES[tier];
42
+ if (!policy) {
43
+ console.error(`❌ Unknown tier: ${tier}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ console.log(`📋 Tier ${tier} Policy:`);
48
+ console.log(`Branch Coverage: ≥${policy.branch_coverage * 100}%`);
49
+ console.log(`Mutation Score: ≥${policy.mutation_score * 100}%`);
50
+ console.log(`Max Files: ${policy.max_files}`);
51
+ console.log(`Max LOC: ${policy.max_loc}`);
52
+ console.log(`Trust Score: ≥${policy.trust_score}`);
53
+ console.log('Requires Contracts: true');
54
+ console.log('Manual Review: Required');
55
+ }
56
+
57
+ /**
58
+ * Enforce coverage gate
59
+ * @param {number} coverage - Coverage value to test
60
+ * @param {number} threshold - Threshold to test against
61
+ */
62
+ function enforceCoverageGate(coverage, threshold = 0.8) {
63
+ if (coverage >= threshold) {
64
+ console.log(`✅ Branch coverage gate passed: ${coverage} >= ${threshold}`);
65
+ return true;
66
+ } else {
67
+ console.log(`❌ Branch coverage gate failed: ${coverage} < ${threshold}`);
68
+ return false;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Enforce mutation gate
74
+ * @param {number} score - Mutation score to test
75
+ * @param {number} threshold - Threshold to test against
76
+ */
77
+ function enforceMutationGate(score, threshold = 0.5) {
78
+ if (score >= threshold) {
79
+ console.log(`✅ Mutation gate passed: ${score} >= ${threshold}`);
80
+ return true;
81
+ } else {
82
+ console.log(`❌ Mutation gate failed: ${score} < ${threshold}`);
83
+ return false;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Enforce trust score gate
89
+ * @param {number} score - Trust score to test
90
+ * @param {number} threshold - Threshold to test against
91
+ */
92
+ function enforceTrustScoreGate(score, threshold = 82) {
93
+ if (score >= threshold) {
94
+ console.log(`✅ Trust score gate passed: ${score} >= ${threshold}`);
95
+ return true;
96
+ } else {
97
+ console.log(`❌ Trust score gate failed: ${score} < ${threshold}`);
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Enforce budget gate
104
+ * @param {number} files - File count to test
105
+ * @param {number} loc - Lines of code to test
106
+ * @param {number} maxFiles - Maximum allowed files
107
+ * @param {number} maxLoc - Maximum allowed LOC
108
+ */
109
+ function enforceBudgetGate(files, loc, maxFiles = 25, maxLoc = 1000) {
110
+ const filesOk = files <= maxFiles;
111
+ const locOk = loc <= maxLoc;
112
+
113
+ if (filesOk && locOk) {
114
+ console.log(`✅ Budget gate passed: ${files} files, ${loc} LOC`);
115
+ return true;
116
+ } else {
117
+ if (!filesOk) {
118
+ console.log(`❌ Budget gate failed: ${files} files > ${maxFiles} max files`);
119
+ }
120
+ if (!locOk) {
121
+ console.log(`❌ Budget gate failed: ${loc} LOC > ${maxLoc} max LOC`);
122
+ }
123
+ return false;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Main command handler
129
+ */
130
+ function main() {
131
+ const command = process.argv[2];
132
+
133
+ switch (command) {
134
+ case 'tier':
135
+ const tier = parseInt(process.argv[3]) || 1;
136
+ showTierPolicy(tier);
137
+ break;
138
+
139
+ case 'coverage':
140
+ const coverage = parseFloat(process.argv[3]) || 0.85;
141
+ const coverageThreshold = 0.8;
142
+ if (!enforceCoverageGate(coverage, coverageThreshold)) {
143
+ throw new Error(`Coverage gate failed: ${coverage} < ${coverageThreshold}`);
144
+ }
145
+ break;
146
+
147
+ case 'mutation':
148
+ const mutationScore = parseFloat(process.argv[3]) || 0.6;
149
+ const mutationThreshold = 0.5;
150
+ if (!enforceMutationGate(mutationScore, mutationThreshold)) {
151
+ throw new Error(`Mutation gate failed: ${mutationScore} < ${mutationThreshold}`);
152
+ }
153
+ break;
154
+
155
+ case 'trust':
156
+ const trustScore = parseInt(process.argv[3]) || 85;
157
+ const trustThreshold = 82;
158
+ if (!enforceTrustScoreGate(trustScore, trustThreshold)) {
159
+ throw new Error(`Trust score gate failed: ${trustScore} < ${trustThreshold}`);
160
+ }
161
+ break;
162
+
163
+ case 'budget':
164
+ const files = parseInt(process.argv[3]) || 20;
165
+ const loc = parseInt(process.argv[4]) || 800;
166
+ if (!enforceBudgetGate(files, loc, 25, 1000)) {
167
+ throw new Error(`Budget gate failed: ${files} files or ${loc} LOC exceeds limits`);
168
+ }
169
+ break;
170
+
171
+ default:
172
+ console.log('CAWS Gates Tool - Quality Gate Enforcement');
173
+ console.log('');
174
+ console.log('Commands:');
175
+ console.log(' tier <tier> - Show tier policy');
176
+ console.log(' coverage <score> - Enforce coverage gate');
177
+ console.log(' mutation <score> - Enforce mutation gate');
178
+ console.log(' trust <score> - Enforce trust score gate');
179
+ console.log(' budget <files> <loc> - Enforce budget gate');
180
+ console.log('');
181
+ console.log('Note: For enhanced features, use gates.ts with: npx tsx gates.ts');
182
+ process.exit(1);
183
+ }
184
+ }
185
+
186
+ // Handle direct script execution
187
+ if (require.main === module) {
188
+ main();
189
+ }
190
+
191
+ module.exports = {
192
+ showTierPolicy,
193
+ enforceCoverageGate,
194
+ enforceMutationGate,
195
+ enforceTrustScoreGate,
196
+ enforceBudgetGate,
197
+ TIER_POLICIES,
198
+ };