@paths.design/caws-cli 2.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1463 -121
- package/package.json +3 -2
- package/templates/agents.md +820 -0
- package/templates/apps/tools/caws/COMPLETION_REPORT.md +331 -0
- package/templates/apps/tools/caws/MIGRATION_SUMMARY.md +360 -0
- package/templates/apps/tools/caws/README.md +463 -0
- package/templates/apps/tools/caws/TEST_STATUS.md +365 -0
- package/templates/apps/tools/caws/attest.js +357 -0
- package/templates/apps/tools/caws/ci-optimizer.js +642 -0
- package/templates/apps/tools/caws/config.ts +245 -0
- package/templates/apps/tools/caws/cross-functional.js +876 -0
- package/templates/apps/tools/caws/dashboard.js +1112 -0
- package/templates/apps/tools/caws/flake-detector.ts +362 -0
- package/templates/apps/tools/caws/gates.js +198 -0
- package/templates/apps/tools/caws/gates.ts +237 -0
- package/templates/apps/tools/caws/language-adapters.ts +381 -0
- package/templates/apps/tools/caws/language-support.d.ts +367 -0
- package/templates/apps/tools/caws/language-support.d.ts.map +1 -0
- package/templates/apps/tools/caws/language-support.js +585 -0
- package/templates/apps/tools/caws/legacy-assessment.ts +408 -0
- package/templates/apps/tools/caws/legacy-assessor.js +764 -0
- package/templates/apps/tools/caws/mutant-analyzer.js +734 -0
- package/templates/apps/tools/caws/perf-budgets.ts +349 -0
- package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
- package/templates/apps/tools/caws/property-testing.js +707 -0
- package/templates/apps/tools/caws/provenance.d.ts +14 -0
- package/templates/apps/tools/caws/provenance.d.ts.map +1 -0
- package/templates/apps/tools/caws/provenance.js +132 -0
- package/templates/apps/tools/caws/provenance.js.backup +73 -0
- package/templates/apps/tools/caws/provenance.ts +211 -0
- package/templates/apps/tools/caws/schemas/waivers.schema.json +30 -0
- package/templates/apps/tools/caws/schemas/working-spec.schema.json +115 -0
- package/templates/apps/tools/caws/scope-guard.js +208 -0
- package/templates/apps/tools/caws/security-provenance.ts +483 -0
- package/templates/apps/tools/caws/shared/base-tool.ts +281 -0
- package/templates/apps/tools/caws/shared/config-manager.ts +366 -0
- package/templates/apps/tools/caws/shared/gate-checker.ts +597 -0
- package/templates/apps/tools/caws/shared/types.ts +444 -0
- package/templates/apps/tools/caws/shared/validator.ts +305 -0
- package/templates/apps/tools/caws/shared/waivers-manager.ts +174 -0
- package/templates/apps/tools/caws/spec-test-mapper.ts +391 -0
- package/templates/apps/tools/caws/templates/working-spec.template.yml +60 -0
- package/templates/apps/tools/caws/test-quality.js +578 -0
- package/templates/apps/tools/caws/tools-allow.json +331 -0
- package/templates/apps/tools/caws/validate.js +76 -0
- package/templates/apps/tools/caws/validate.ts +228 -0
- package/templates/apps/tools/caws/waivers.js +344 -0
- package/templates/apps/tools/caws/waivers.yml +19 -0
- package/templates/codemod/README.md +1 -0
- package/templates/codemod/test.js +1 -0
- package/templates/docs/README.md +150 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CAWS Gates Tool - Enhanced Implementation
|
|
5
|
+
* CLI wrapper for CawsGateChecker with tier policies
|
|
6
|
+
*
|
|
7
|
+
* @author @darianrosebrook
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { CawsGateChecker } from './shared/gate-checker.js';
|
|
11
|
+
import { CawsConfigManager } from './shared/config-manager.js';
|
|
12
|
+
|
|
13
|
+
// Tier policies for quality gates
|
|
14
|
+
const TIER_POLICIES = {
|
|
15
|
+
1: {
|
|
16
|
+
branch_coverage: 0.9,
|
|
17
|
+
mutation_score: 0.7,
|
|
18
|
+
max_files: 40,
|
|
19
|
+
max_loc: 1500,
|
|
20
|
+
trust_score: 85,
|
|
21
|
+
},
|
|
22
|
+
2: {
|
|
23
|
+
branch_coverage: 0.8,
|
|
24
|
+
mutation_score: 0.5,
|
|
25
|
+
max_files: 25,
|
|
26
|
+
max_loc: 1000,
|
|
27
|
+
trust_score: 82,
|
|
28
|
+
},
|
|
29
|
+
3: {
|
|
30
|
+
branch_coverage: 0.7,
|
|
31
|
+
mutation_score: 0.3,
|
|
32
|
+
max_files: 15,
|
|
33
|
+
max_loc: 500,
|
|
34
|
+
trust_score: 75,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class GatesCLI {
|
|
39
|
+
private gateChecker: CawsGateChecker;
|
|
40
|
+
private configManager: CawsConfigManager;
|
|
41
|
+
|
|
42
|
+
constructor() {
|
|
43
|
+
this.gateChecker = new CawsGateChecker();
|
|
44
|
+
this.configManager = new CawsConfigManager();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Show tier policy
|
|
49
|
+
*/
|
|
50
|
+
showTierPolicy(tier: number = 1): void {
|
|
51
|
+
const policy = TIER_POLICIES[tier as keyof typeof TIER_POLICIES];
|
|
52
|
+
if (!policy) {
|
|
53
|
+
console.error(`❌ Unknown tier: ${tier}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`📋 Tier ${tier} Policy:`);
|
|
58
|
+
console.log(`Branch Coverage: ≥${policy.branch_coverage * 100}%`);
|
|
59
|
+
console.log(`Mutation Score: ≥${policy.mutation_score * 100}%`);
|
|
60
|
+
console.log(`Max Files: ${policy.max_files}`);
|
|
61
|
+
console.log(`Max LOC: ${policy.max_loc}`);
|
|
62
|
+
console.log(`Trust Score: ≥${policy.trust_score}`);
|
|
63
|
+
console.log('Requires Contracts: true');
|
|
64
|
+
console.log('Manual Review: Required');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Enforce coverage gate using CawsGateChecker
|
|
69
|
+
*/
|
|
70
|
+
async enforceCoverageGate(tier: number = 2): Promise<boolean> {
|
|
71
|
+
try {
|
|
72
|
+
const policy = TIER_POLICIES[tier as keyof typeof TIER_POLICIES];
|
|
73
|
+
const result = await this.gateChecker.checkCoverage({
|
|
74
|
+
tier,
|
|
75
|
+
workingDirectory: process.cwd(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (result.passed) {
|
|
79
|
+
console.log(
|
|
80
|
+
`✅ Coverage gate passed: ${(result.score * 100).toFixed(1)}% ≥ ${policy.branch_coverage * 100}%`
|
|
81
|
+
);
|
|
82
|
+
return true;
|
|
83
|
+
} else {
|
|
84
|
+
console.log(
|
|
85
|
+
`❌ Coverage gate failed: ${(result.score * 100).toFixed(1)}% < ${policy.branch_coverage * 100}%`
|
|
86
|
+
);
|
|
87
|
+
if (result.errors && result.errors.length > 0) {
|
|
88
|
+
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(`❌ Coverage gate check failed: ${error}`);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Enforce mutation gate using CawsGateChecker
|
|
100
|
+
*/
|
|
101
|
+
async enforceMutationGate(tier: number = 2): Promise<boolean> {
|
|
102
|
+
try {
|
|
103
|
+
const policy = TIER_POLICIES[tier as keyof typeof TIER_POLICIES];
|
|
104
|
+
const result = await this.gateChecker.checkMutation({
|
|
105
|
+
tier,
|
|
106
|
+
workingDirectory: process.cwd(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (result.passed) {
|
|
110
|
+
console.log(
|
|
111
|
+
`✅ Mutation gate passed: ${(result.score * 100).toFixed(1)}% ≥ ${policy.mutation_score * 100}%`
|
|
112
|
+
);
|
|
113
|
+
return true;
|
|
114
|
+
} else {
|
|
115
|
+
console.log(
|
|
116
|
+
`❌ Mutation gate failed: ${(result.score * 100).toFixed(1)}% < ${policy.mutation_score * 100}%`
|
|
117
|
+
);
|
|
118
|
+
if (result.errors && result.errors.length > 0) {
|
|
119
|
+
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(`❌ Mutation gate check failed: ${error}`);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Enforce contract gate using CawsGateChecker
|
|
131
|
+
*/
|
|
132
|
+
async enforceContractGate(tier: number = 2): Promise<boolean> {
|
|
133
|
+
try {
|
|
134
|
+
const result = await this.gateChecker.checkContracts({
|
|
135
|
+
tier,
|
|
136
|
+
workingDirectory: process.cwd(),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (result.passed) {
|
|
140
|
+
console.log('✅ Contract gate passed');
|
|
141
|
+
return true;
|
|
142
|
+
} else {
|
|
143
|
+
console.log('❌ Contract gate failed');
|
|
144
|
+
if (result.errors && result.errors.length > 0) {
|
|
145
|
+
result.errors.forEach((error) => console.error(` - ${error}`));
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(`❌ Contract gate check failed: ${error}`);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Run all gates for a tier
|
|
157
|
+
*/
|
|
158
|
+
async runAllGates(tier: number = 2): Promise<boolean> {
|
|
159
|
+
console.log(`\n🚪 Running all gates for Tier ${tier}...\n`);
|
|
160
|
+
|
|
161
|
+
const results = await Promise.all([
|
|
162
|
+
this.enforceCoverageGate(tier),
|
|
163
|
+
this.enforceMutationGate(tier),
|
|
164
|
+
this.enforceContractGate(tier),
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const allPassed = results.every((r) => r);
|
|
168
|
+
|
|
169
|
+
console.log(`\n${'='.repeat(50)}`);
|
|
170
|
+
if (allPassed) {
|
|
171
|
+
console.log('✅ All gates passed!');
|
|
172
|
+
} else {
|
|
173
|
+
console.log('❌ Some gates failed');
|
|
174
|
+
}
|
|
175
|
+
console.log('='.repeat(50));
|
|
176
|
+
|
|
177
|
+
return allPassed;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Main CLI handler
|
|
182
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
183
|
+
(async () => {
|
|
184
|
+
const command = process.argv[2];
|
|
185
|
+
const cli = new GatesCLI();
|
|
186
|
+
|
|
187
|
+
switch (command) {
|
|
188
|
+
case 'tier': {
|
|
189
|
+
const tier = parseInt(process.argv[3]) || 1;
|
|
190
|
+
cli.showTierPolicy(tier);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
case 'coverage': {
|
|
195
|
+
const tier = parseInt(process.argv[3]) || 2;
|
|
196
|
+
const passed = await cli.enforceCoverageGate(tier);
|
|
197
|
+
process.exit(passed ? 0 : 1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case 'mutation': {
|
|
201
|
+
const tier = parseInt(process.argv[3]) || 2;
|
|
202
|
+
const passed = await cli.enforceMutationGate(tier);
|
|
203
|
+
process.exit(passed ? 0 : 1);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
case 'contracts': {
|
|
207
|
+
const tier = parseInt(process.argv[3]) || 2;
|
|
208
|
+
const passed = await cli.enforceContractGate(tier);
|
|
209
|
+
process.exit(passed ? 0 : 1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
case 'all': {
|
|
213
|
+
const tier = parseInt(process.argv[3]) || 2;
|
|
214
|
+
const passed = await cli.runAllGates(tier);
|
|
215
|
+
process.exit(passed ? 0 : 1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
default:
|
|
219
|
+
console.log('CAWS Gates Tool - Quality Gate Enforcement');
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log('Commands:');
|
|
222
|
+
console.log(' tier <tier> - Show tier policy');
|
|
223
|
+
console.log(' coverage <tier> - Enforce coverage gate');
|
|
224
|
+
console.log(' mutation <tier> - Enforce mutation gate');
|
|
225
|
+
console.log(' contracts <tier> - Enforce contract gate');
|
|
226
|
+
console.log(' all <tier> - Run all gates for tier');
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log('Examples:');
|
|
229
|
+
console.log(' gates.ts tier 1');
|
|
230
|
+
console.log(' gates.ts coverage 2');
|
|
231
|
+
console.log(' gates.ts all 2');
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { GatesCLI };
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CAWS Language Adapter Manager
|
|
5
|
+
* Multi-language support for TypeScript, Python, Rust, Go, Java
|
|
6
|
+
*
|
|
7
|
+
* @author @darianrosebrook
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { CawsBaseTool } from './shared/base-tool.js';
|
|
12
|
+
import { TierPolicy, GateConfig } from './shared/types.js';
|
|
13
|
+
|
|
14
|
+
interface LanguageAdapter {
|
|
15
|
+
language: string;
|
|
16
|
+
fileExtensions: string[];
|
|
17
|
+
tools: {
|
|
18
|
+
unitTest: { command: string; args: string[] };
|
|
19
|
+
coverage: { command: string; args: string[] };
|
|
20
|
+
mutationTest?: { command: string; args: string[] };
|
|
21
|
+
contractTest?: { command: string; args: string[] };
|
|
22
|
+
lint: { command: string; args: string[] };
|
|
23
|
+
};
|
|
24
|
+
tierAdjustments?: {
|
|
25
|
+
[tier: number]: Partial<TierPolicy>;
|
|
26
|
+
};
|
|
27
|
+
fallbacks?: {
|
|
28
|
+
mutation?: string; // What to do if mutation testing unavailable
|
|
29
|
+
contracts?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class LanguageAdapterManager extends CawsBaseTool {
|
|
34
|
+
private adapters: Map<string, LanguageAdapter> = new Map();
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
super();
|
|
38
|
+
this.initializeAdapters();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private initializeAdapters(): void {
|
|
42
|
+
// TypeScript/JavaScript adapter
|
|
43
|
+
this.adapters.set('typescript', {
|
|
44
|
+
language: 'typescript',
|
|
45
|
+
fileExtensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
46
|
+
tools: {
|
|
47
|
+
unitTest: { command: 'vitest', args: ['run'] },
|
|
48
|
+
coverage: { command: 'vitest', args: ['run', '--coverage'] },
|
|
49
|
+
mutationTest: { command: 'stryker', args: ['run'] },
|
|
50
|
+
contractTest: { command: 'pact', args: ['verify'] },
|
|
51
|
+
lint: { command: 'eslint', args: ['.'] },
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Python adapter
|
|
56
|
+
this.adapters.set('python', {
|
|
57
|
+
language: 'python',
|
|
58
|
+
fileExtensions: ['.py'],
|
|
59
|
+
tools: {
|
|
60
|
+
unitTest: { command: 'pytest', args: [] },
|
|
61
|
+
coverage: { command: 'pytest', args: ['--cov'] },
|
|
62
|
+
mutationTest: { command: 'mutmut', args: ['run'] },
|
|
63
|
+
contractTest: { command: 'schemathesis', args: ['run'] },
|
|
64
|
+
lint: { command: 'ruff', args: ['check', '.'] },
|
|
65
|
+
},
|
|
66
|
+
tierAdjustments: {
|
|
67
|
+
1: { min_mutation: 0.6 }, // Python mutation testing is less mature
|
|
68
|
+
2: { min_mutation: 0.4 },
|
|
69
|
+
3: { min_mutation: 0.25 },
|
|
70
|
+
},
|
|
71
|
+
fallbacks: {
|
|
72
|
+
mutation: 'If mutation testing unavailable, increase integration test coverage by 20%',
|
|
73
|
+
contracts: 'Use pytest with OpenAPI schema validation',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Rust adapter
|
|
78
|
+
this.adapters.set('rust', {
|
|
79
|
+
language: 'rust',
|
|
80
|
+
fileExtensions: ['.rs'],
|
|
81
|
+
tools: {
|
|
82
|
+
unitTest: { command: 'cargo', args: ['test'] },
|
|
83
|
+
coverage: { command: 'cargo', args: ['tarpaulin', '--out', 'Json'] },
|
|
84
|
+
mutationTest: { command: 'cargo', args: ['mutants'] },
|
|
85
|
+
lint: { command: 'cargo', args: ['clippy', '--', '-D', 'warnings'] },
|
|
86
|
+
},
|
|
87
|
+
tierAdjustments: {
|
|
88
|
+
1: { min_mutation: 0.65 },
|
|
89
|
+
2: { min_mutation: 0.45 },
|
|
90
|
+
3: { min_mutation: 0.3 },
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Go adapter
|
|
95
|
+
this.adapters.set('go', {
|
|
96
|
+
language: 'go',
|
|
97
|
+
fileExtensions: ['.go'],
|
|
98
|
+
tools: {
|
|
99
|
+
unitTest: { command: 'go', args: ['test', './...'] },
|
|
100
|
+
coverage: {
|
|
101
|
+
command: 'go',
|
|
102
|
+
args: ['test', '-coverprofile=coverage.out', './...'],
|
|
103
|
+
},
|
|
104
|
+
lint: { command: 'golangci-lint', args: ['run'] },
|
|
105
|
+
},
|
|
106
|
+
fallbacks: {
|
|
107
|
+
mutation: 'Go lacks mature mutation testing - require 90% branch coverage instead',
|
|
108
|
+
contracts: 'Use go-swagger for API contract validation',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Java adapter
|
|
113
|
+
this.adapters.set('java', {
|
|
114
|
+
language: 'java',
|
|
115
|
+
fileExtensions: ['.java'],
|
|
116
|
+
tools: {
|
|
117
|
+
unitTest: { command: 'mvn', args: ['test'] },
|
|
118
|
+
coverage: { command: 'mvn', args: ['jacoco:report'] },
|
|
119
|
+
mutationTest: {
|
|
120
|
+
command: 'mvn',
|
|
121
|
+
args: ['org.pitest:pitest-maven:mutationCoverage'],
|
|
122
|
+
},
|
|
123
|
+
contractTest: { command: 'mvn', args: ['pact:verify'] },
|
|
124
|
+
lint: { command: 'mvn', args: ['checkstyle:check'] },
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Detect project language based on files present
|
|
131
|
+
*/
|
|
132
|
+
detectLanguage(projectDir: string): string | null {
|
|
133
|
+
const indicators = [
|
|
134
|
+
{ file: 'package.json', language: 'typescript' },
|
|
135
|
+
{ file: 'tsconfig.json', language: 'typescript' },
|
|
136
|
+
{ file: 'requirements.txt', language: 'python' },
|
|
137
|
+
{ file: 'pyproject.toml', language: 'python' },
|
|
138
|
+
{ file: 'Pipfile', language: 'python' },
|
|
139
|
+
{ file: 'Cargo.toml', language: 'rust' },
|
|
140
|
+
{ file: 'go.mod', language: 'go' },
|
|
141
|
+
{ file: 'pom.xml', language: 'java' },
|
|
142
|
+
{ file: 'build.gradle', language: 'java' },
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
for (const indicator of indicators) {
|
|
146
|
+
const filePath = path.join(projectDir, indicator.file);
|
|
147
|
+
if (this.pathExists(filePath)) {
|
|
148
|
+
return indicator.language;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get adapter for a specific language
|
|
157
|
+
*/
|
|
158
|
+
getAdapter(language: string): LanguageAdapter | null {
|
|
159
|
+
return this.adapters.get(language.toLowerCase()) || null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get adjusted tier policy for a language
|
|
164
|
+
*/
|
|
165
|
+
getAdjustedTierPolicy(language: string, tier: number): TierPolicy | null {
|
|
166
|
+
const adapter = this.getAdapter(language);
|
|
167
|
+
if (!adapter) return null;
|
|
168
|
+
|
|
169
|
+
const basePolicies: Record<number, TierPolicy> = {
|
|
170
|
+
1: {
|
|
171
|
+
min_branch: 0.9,
|
|
172
|
+
min_coverage: 0.9,
|
|
173
|
+
min_mutation: 0.7,
|
|
174
|
+
requires_contracts: true,
|
|
175
|
+
requires_manual_review: true,
|
|
176
|
+
},
|
|
177
|
+
2: {
|
|
178
|
+
min_branch: 0.8,
|
|
179
|
+
min_coverage: 0.8,
|
|
180
|
+
min_mutation: 0.5,
|
|
181
|
+
requires_contracts: true,
|
|
182
|
+
},
|
|
183
|
+
3: {
|
|
184
|
+
min_branch: 0.7,
|
|
185
|
+
min_coverage: 0.7,
|
|
186
|
+
min_mutation: 0.3,
|
|
187
|
+
requires_contracts: false,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const basePolicy = basePolicies[tier];
|
|
192
|
+
if (!basePolicy) return null;
|
|
193
|
+
|
|
194
|
+
// Apply language-specific adjustments
|
|
195
|
+
const adjustments = adapter.tierAdjustments?.[tier] || {};
|
|
196
|
+
return { ...basePolicy, ...adjustments };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generate language-specific configuration
|
|
201
|
+
*/
|
|
202
|
+
generateConfig(language: string): Record<string, any> | null {
|
|
203
|
+
const adapter = this.getAdapter(language);
|
|
204
|
+
if (!adapter) return null;
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
language: adapter.language,
|
|
208
|
+
tools: adapter.tools,
|
|
209
|
+
gates: this.generateGateConfig(adapter),
|
|
210
|
+
tierAdjustments: adapter.tierAdjustments,
|
|
211
|
+
fallbacks: adapter.fallbacks,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private generateGateConfig(adapter: LanguageAdapter): Record<string, GateConfig> {
|
|
216
|
+
const gates: Record<string, GateConfig> = {
|
|
217
|
+
coverage: {
|
|
218
|
+
enabled: true,
|
|
219
|
+
threshold: 0.8,
|
|
220
|
+
},
|
|
221
|
+
mutation: {
|
|
222
|
+
enabled: !!adapter.tools.mutationTest,
|
|
223
|
+
threshold: 0.5,
|
|
224
|
+
},
|
|
225
|
+
contracts: {
|
|
226
|
+
enabled: !!adapter.tools.contractTest,
|
|
227
|
+
threshold: 1.0,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
return gates;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* List all available adapters
|
|
236
|
+
*/
|
|
237
|
+
listAdapters(): Array<{
|
|
238
|
+
language: string;
|
|
239
|
+
hasMutation: boolean;
|
|
240
|
+
hasContracts: boolean;
|
|
241
|
+
}> {
|
|
242
|
+
return Array.from(this.adapters.values()).map((adapter) => ({
|
|
243
|
+
language: adapter.language,
|
|
244
|
+
hasMutation: !!adapter.tools.mutationTest,
|
|
245
|
+
hasContracts: !!adapter.tools.contractTest,
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Check if tools are available for a language
|
|
251
|
+
*/
|
|
252
|
+
checkToolAvailability(language: string): {
|
|
253
|
+
language: string;
|
|
254
|
+
available: Record<string, boolean>;
|
|
255
|
+
missing: string[];
|
|
256
|
+
} {
|
|
257
|
+
const adapter = this.getAdapter(language);
|
|
258
|
+
if (!adapter) {
|
|
259
|
+
return { language, available: {}, missing: [] };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const available: Record<string, boolean> = {};
|
|
263
|
+
const missing: string[] = [];
|
|
264
|
+
|
|
265
|
+
const toolChecks = [
|
|
266
|
+
{ name: 'unitTest', tool: adapter.tools.unitTest },
|
|
267
|
+
{ name: 'coverage', tool: adapter.tools.coverage },
|
|
268
|
+
{ name: 'mutationTest', tool: adapter.tools.mutationTest },
|
|
269
|
+
{ name: 'contractTest', tool: adapter.tools.contractTest },
|
|
270
|
+
{ name: 'lint', tool: adapter.tools.lint },
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
for (const { name, tool } of toolChecks) {
|
|
274
|
+
if (tool) {
|
|
275
|
+
// Simple check - just verify command exists (would need proper implementation)
|
|
276
|
+
available[name] = true; // Placeholder
|
|
277
|
+
} else {
|
|
278
|
+
available[name] = false;
|
|
279
|
+
missing.push(name);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { language, available, missing };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// CLI interface
|
|
288
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
289
|
+
const command = process.argv[2];
|
|
290
|
+
const manager = new LanguageAdapterManager();
|
|
291
|
+
|
|
292
|
+
switch (command) {
|
|
293
|
+
case 'detect': {
|
|
294
|
+
const projectDir = process.argv[3] || process.cwd();
|
|
295
|
+
const language = manager.detectLanguage(projectDir);
|
|
296
|
+
|
|
297
|
+
if (language) {
|
|
298
|
+
console.log(`✅ Detected language: ${language}`);
|
|
299
|
+
const adapter = manager.getAdapter(language);
|
|
300
|
+
if (adapter) {
|
|
301
|
+
console.log(`\n📦 Available tools:`);
|
|
302
|
+
console.log(` Unit tests: ${adapter.tools.unitTest.command}`);
|
|
303
|
+
console.log(` Coverage: ${adapter.tools.coverage.command}`);
|
|
304
|
+
if (adapter.tools.mutationTest) {
|
|
305
|
+
console.log(` Mutation: ${adapter.tools.mutationTest.command}`);
|
|
306
|
+
}
|
|
307
|
+
if (adapter.tools.contractTest) {
|
|
308
|
+
console.log(` Contracts: ${adapter.tools.contractTest.command}`);
|
|
309
|
+
}
|
|
310
|
+
console.log(` Lint: ${adapter.tools.lint.command}`);
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
console.log('❌ Could not detect language');
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case 'list': {
|
|
320
|
+
const adapters = manager.listAdapters();
|
|
321
|
+
console.log('Available Language Adapters:');
|
|
322
|
+
console.log('='.repeat(50));
|
|
323
|
+
adapters.forEach((adapter) => {
|
|
324
|
+
console.log(`\n📚 ${adapter.language}`);
|
|
325
|
+
console.log(` Mutation testing: ${adapter.hasMutation ? '✅' : '❌'}`);
|
|
326
|
+
console.log(` Contract testing: ${adapter.hasContracts ? '✅' : '❌'}`);
|
|
327
|
+
});
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
case 'config': {
|
|
332
|
+
const language = process.argv[3];
|
|
333
|
+
if (!language) {
|
|
334
|
+
console.error('Usage: language-adapters config <language>');
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const config = manager.generateConfig(language);
|
|
339
|
+
if (config) {
|
|
340
|
+
console.log(JSON.stringify(config, null, 2));
|
|
341
|
+
} else {
|
|
342
|
+
console.error(`❌ No adapter found for language: ${language}`);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
case 'tier': {
|
|
349
|
+
const language = process.argv[3];
|
|
350
|
+
const tier = parseInt(process.argv[4]);
|
|
351
|
+
|
|
352
|
+
if (!language || !tier) {
|
|
353
|
+
console.error('Usage: language-adapters tier <language> <tier>');
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const policy = manager.getAdjustedTierPolicy(language, tier);
|
|
358
|
+
if (policy) {
|
|
359
|
+
console.log(`Tier ${tier} policy for ${language}:`);
|
|
360
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
361
|
+
} else {
|
|
362
|
+
console.error(`❌ Could not get policy for ${language} tier ${tier}`);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
default:
|
|
369
|
+
console.log('CAWS Language Adapter Manager');
|
|
370
|
+
console.log('');
|
|
371
|
+
console.log('Usage:');
|
|
372
|
+
console.log(' language-adapters detect [dir] - Detect project language');
|
|
373
|
+
console.log(' language-adapters list - List available adapters');
|
|
374
|
+
console.log(' language-adapters config <language> - Generate config for language');
|
|
375
|
+
console.log(' language-adapters tier <language> <tier> - Get tier policy for language');
|
|
376
|
+
console.log('');
|
|
377
|
+
console.log('Supported Languages:');
|
|
378
|
+
console.log(' typescript, python, rust, go, java');
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
}
|