@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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -96
- package/package.json +3 -3
- 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/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.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,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAWS Base Tool
|
|
3
|
+
* Shared functionality for all CAWS tools including file operations,
|
|
4
|
+
* configuration management, and common utilities
|
|
5
|
+
*
|
|
6
|
+
* @author @darianrosebrook
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { CawsConfig } from './types.js';
|
|
13
|
+
|
|
14
|
+
export interface ToolResult {
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
data?: any;
|
|
18
|
+
errors?: string[];
|
|
19
|
+
warnings?: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FileOperationOptions {
|
|
23
|
+
encoding?: string;
|
|
24
|
+
createDir?: boolean;
|
|
25
|
+
backup?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export abstract class CawsBaseTool {
|
|
29
|
+
protected readonly __dirname: string;
|
|
30
|
+
protected readonly cawsDirectory: string;
|
|
31
|
+
protected readonly workingDirectory: string;
|
|
32
|
+
|
|
33
|
+
constructor() {
|
|
34
|
+
this.__dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
this.workingDirectory = process.cwd();
|
|
36
|
+
this.cawsDirectory = path.join(this.workingDirectory, '.caws');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get the CAWS configuration directory
|
|
41
|
+
*/
|
|
42
|
+
protected getCawsDirectory(): string {
|
|
43
|
+
return this.cawsDirectory;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the working directory
|
|
48
|
+
*/
|
|
49
|
+
protected getWorkingDirectory(): string {
|
|
50
|
+
return this.workingDirectory;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Safely read a JSON file with error handling
|
|
55
|
+
*/
|
|
56
|
+
protected readJsonFile<T = any>(filePath: string): T | null {
|
|
57
|
+
try {
|
|
58
|
+
if (!fs.existsSync(filePath)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
63
|
+
return JSON.parse(content) as T;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
this.logError(`Failed to read JSON file ${filePath}: ${error}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Safely write a JSON file with backup option
|
|
72
|
+
*/
|
|
73
|
+
protected writeJsonFile(
|
|
74
|
+
filePath: string,
|
|
75
|
+
data: any,
|
|
76
|
+
options: FileOperationOptions = {}
|
|
77
|
+
): boolean {
|
|
78
|
+
try {
|
|
79
|
+
const { createDir = true, backup = false } = options;
|
|
80
|
+
|
|
81
|
+
// Create directory if needed
|
|
82
|
+
if (createDir) {
|
|
83
|
+
const dir = path.dirname(filePath);
|
|
84
|
+
if (!fs.existsSync(dir)) {
|
|
85
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create backup if requested
|
|
90
|
+
if (backup && fs.existsSync(filePath)) {
|
|
91
|
+
const backupPath = `${filePath}.backup`;
|
|
92
|
+
fs.copyFileSync(filePath, backupPath);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Write the file
|
|
96
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
97
|
+
return true;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
this.logError(`Failed to write JSON file ${filePath}: ${error}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Safely read a YAML file
|
|
106
|
+
*/
|
|
107
|
+
protected async readYamlFile<T = any>(filePath: string): Promise<T | null> {
|
|
108
|
+
try {
|
|
109
|
+
if (!fs.existsSync(filePath)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
114
|
+
const yaml = await import('js-yaml');
|
|
115
|
+
return yaml.load(content) as T;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.logError(`Failed to read YAML file ${error}`);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a path exists
|
|
124
|
+
*/
|
|
125
|
+
protected pathExists(filePath: string): boolean {
|
|
126
|
+
return fs.existsSync(filePath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create directory if it doesn't exist
|
|
131
|
+
*/
|
|
132
|
+
protected ensureDirectoryExists(dirPath: string): boolean {
|
|
133
|
+
try {
|
|
134
|
+
if (!fs.existsSync(dirPath)) {
|
|
135
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.logError(`Failed to create directory ${dirPath}: ${error}`);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get relative path from working directory
|
|
146
|
+
*/
|
|
147
|
+
protected getRelativePath(absolutePath: string): string {
|
|
148
|
+
return path.relative(this.workingDirectory, absolutePath);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get absolute path from relative path
|
|
153
|
+
*/
|
|
154
|
+
protected getAbsolutePath(relativePath: string): string {
|
|
155
|
+
return path.resolve(this.workingDirectory, relativePath);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Load tier policy configuration
|
|
160
|
+
*/
|
|
161
|
+
protected loadTierPolicy(): Record<string, any> | null {
|
|
162
|
+
const policyPath = path.join(this.cawsDirectory, 'policy', 'tier-policy.json');
|
|
163
|
+
return this.readJsonFile(policyPath);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Load CAWS configuration
|
|
168
|
+
*/
|
|
169
|
+
protected loadCawsConfig(): CawsConfig | null {
|
|
170
|
+
const configPath = path.join(this.cawsDirectory, 'config.json');
|
|
171
|
+
return this.readJsonFile(configPath);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Log an error message
|
|
176
|
+
*/
|
|
177
|
+
protected logError(message: string): void {
|
|
178
|
+
console.error(`❌ ${message}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Log a warning message
|
|
183
|
+
*/
|
|
184
|
+
protected logWarning(message: string): void {
|
|
185
|
+
console.warn(`⚠️ ${message}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Log an info message
|
|
190
|
+
*/
|
|
191
|
+
protected logInfo(message: string): void {
|
|
192
|
+
console.log(`ℹ️ ${message}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Log a success message
|
|
197
|
+
*/
|
|
198
|
+
protected logSuccess(message: string): void {
|
|
199
|
+
console.log(`✅ ${message}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create a standardized result object
|
|
204
|
+
*/
|
|
205
|
+
protected createResult(
|
|
206
|
+
success: boolean,
|
|
207
|
+
message: string,
|
|
208
|
+
data?: any,
|
|
209
|
+
errors?: string[],
|
|
210
|
+
warnings?: string[]
|
|
211
|
+
): ToolResult {
|
|
212
|
+
return {
|
|
213
|
+
success,
|
|
214
|
+
message,
|
|
215
|
+
data,
|
|
216
|
+
errors: errors || [],
|
|
217
|
+
warnings: warnings || [],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validate required environment variables
|
|
223
|
+
*/
|
|
224
|
+
protected validateEnvironment(variables: string[]): boolean {
|
|
225
|
+
const missing = variables.filter((varName) => !process.env[varName]);
|
|
226
|
+
|
|
227
|
+
if (missing.length > 0) {
|
|
228
|
+
this.logError(`Missing required environment variables: ${missing.join(', ')}`);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get environment variable with fallback
|
|
237
|
+
*/
|
|
238
|
+
protected getEnvVar(name: string, fallback: string = ''): string {
|
|
239
|
+
return process.env[name] || fallback;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Parse command line arguments
|
|
244
|
+
*/
|
|
245
|
+
protected parseArgs(expectedArgs: string[]): Record<string, string> {
|
|
246
|
+
const args = process.argv.slice(2);
|
|
247
|
+
const result: Record<string, string> = {};
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < args.length; i++) {
|
|
250
|
+
if (i < expectedArgs.length) {
|
|
251
|
+
result[expectedArgs[i]] = args[i];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Show usage information
|
|
260
|
+
*/
|
|
261
|
+
protected showUsage(usage: string, description: string): void {
|
|
262
|
+
console.log(`Usage: ${usage}`);
|
|
263
|
+
console.log(description);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Exit with appropriate code
|
|
268
|
+
*/
|
|
269
|
+
protected exitWithResult(result: ToolResult): void {
|
|
270
|
+
if (result.success) {
|
|
271
|
+
this.logSuccess(result.message);
|
|
272
|
+
process.exit(0);
|
|
273
|
+
} else {
|
|
274
|
+
this.logError(result.message);
|
|
275
|
+
if (result.errors && result.errors.length > 0) {
|
|
276
|
+
result.errors.forEach((error) => this.logError(error));
|
|
277
|
+
}
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAWS Configuration Manager
|
|
3
|
+
* Centralized configuration management for CAWS tools
|
|
4
|
+
*
|
|
5
|
+
* @author @darianrosebrook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import yaml from 'js-yaml';
|
|
10
|
+
import { CawsConfig } from './types.js';
|
|
11
|
+
import { CawsBaseTool, ToolResult } from './base-tool.js';
|
|
12
|
+
|
|
13
|
+
export class CawsConfigManager extends CawsBaseTool {
|
|
14
|
+
private config: CawsConfig | null = null;
|
|
15
|
+
private readonly configPath: string;
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.configPath = path.join(this.getCawsDirectory(), 'config.json');
|
|
20
|
+
this.loadConfig();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load configuration from file
|
|
25
|
+
*/
|
|
26
|
+
private loadConfig(): void {
|
|
27
|
+
try {
|
|
28
|
+
const configData = this.readJsonFile(this.configPath);
|
|
29
|
+
if (configData) {
|
|
30
|
+
this.config = configData as CawsConfig;
|
|
31
|
+
this.validateConfig();
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
this.logWarning('Failed to load CAWS configuration, using defaults');
|
|
35
|
+
this.config = this.getDefaultConfig();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Save configuration to file
|
|
41
|
+
*/
|
|
42
|
+
private saveConfig(): boolean {
|
|
43
|
+
try {
|
|
44
|
+
return this.writeJsonFile(this.configPath, this.config);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
this.logError(`Failed to save CAWS configuration: ${error}`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get default configuration
|
|
53
|
+
*/
|
|
54
|
+
private getDefaultConfig(): CawsConfig {
|
|
55
|
+
return {
|
|
56
|
+
version: '1.0.0',
|
|
57
|
+
environment: 'development',
|
|
58
|
+
gates: {
|
|
59
|
+
coverage: {
|
|
60
|
+
enabled: true,
|
|
61
|
+
thresholds: {
|
|
62
|
+
statements: 80,
|
|
63
|
+
branches: 75,
|
|
64
|
+
functions: 80,
|
|
65
|
+
lines: 80,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
mutation: {
|
|
69
|
+
enabled: true,
|
|
70
|
+
thresholds: {
|
|
71
|
+
killed: 70,
|
|
72
|
+
survived: 30,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
contracts: {
|
|
76
|
+
enabled: true,
|
|
77
|
+
required: true,
|
|
78
|
+
},
|
|
79
|
+
trust_score: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
threshold: 0.8,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
tools: {
|
|
85
|
+
coverage: {
|
|
86
|
+
command: 'nyc',
|
|
87
|
+
args: ['--reporter=json', '--reporter=text'],
|
|
88
|
+
},
|
|
89
|
+
mutation: {
|
|
90
|
+
command: 'stryker',
|
|
91
|
+
args: ['run'],
|
|
92
|
+
},
|
|
93
|
+
contracts: {
|
|
94
|
+
command: 'pact',
|
|
95
|
+
args: ['verify'],
|
|
96
|
+
},
|
|
97
|
+
linting: {
|
|
98
|
+
command: 'eslint',
|
|
99
|
+
args: ['.'],
|
|
100
|
+
},
|
|
101
|
+
testing: {
|
|
102
|
+
command: 'jest',
|
|
103
|
+
args: ['--coverage'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
paths: {
|
|
107
|
+
working_directory: this.getWorkingDirectory(),
|
|
108
|
+
reports: path.join(this.getWorkingDirectory(), 'reports'),
|
|
109
|
+
coverage: path.join(this.getWorkingDirectory(), 'coverage'),
|
|
110
|
+
artifacts: path.join(this.getWorkingDirectory(), 'artifacts'),
|
|
111
|
+
},
|
|
112
|
+
logging: {
|
|
113
|
+
level: 'info',
|
|
114
|
+
file: path.join(this.getCawsDirectory(), 'logs', 'caws.log'),
|
|
115
|
+
format: 'json',
|
|
116
|
+
},
|
|
117
|
+
features: {
|
|
118
|
+
multi_modal: true,
|
|
119
|
+
obsidian_support: true,
|
|
120
|
+
parallel_processing: true,
|
|
121
|
+
},
|
|
122
|
+
tiers: {
|
|
123
|
+
1: {
|
|
124
|
+
min_branch: 0.9,
|
|
125
|
+
min_coverage: 0.9,
|
|
126
|
+
min_mutation: 0.8,
|
|
127
|
+
requires_contracts: true,
|
|
128
|
+
},
|
|
129
|
+
2: {
|
|
130
|
+
min_branch: 0.8,
|
|
131
|
+
min_coverage: 0.8,
|
|
132
|
+
min_mutation: 0.7,
|
|
133
|
+
requires_contracts: true,
|
|
134
|
+
},
|
|
135
|
+
3: {
|
|
136
|
+
min_branch: 0.7,
|
|
137
|
+
min_coverage: 0.7,
|
|
138
|
+
min_mutation: 0.6,
|
|
139
|
+
requires_contracts: false,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
defaultTier: '2',
|
|
143
|
+
workingSpecPath: path.join(this.getCawsDirectory(), 'working-spec.yaml'),
|
|
144
|
+
provenancePath: path.join(this.getCawsDirectory(), 'provenance.json'),
|
|
145
|
+
waiversPath: path.join(this.getCawsDirectory(), 'waivers.yml'),
|
|
146
|
+
cawsDirectory: this.getCawsDirectory(),
|
|
147
|
+
experiment_defaults: {
|
|
148
|
+
enabled: false,
|
|
149
|
+
timeboxed_hours: 24,
|
|
150
|
+
success_criteria: ['Basic functionality works'],
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Validate configuration structure
|
|
157
|
+
*/
|
|
158
|
+
private validateConfig(): void {
|
|
159
|
+
if (!this.config) return;
|
|
160
|
+
|
|
161
|
+
// Basic validation
|
|
162
|
+
if (!this.config.version) {
|
|
163
|
+
this.logWarning('Configuration missing version, setting to default');
|
|
164
|
+
this.config.version = '1.0.0';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!this.config.environment) {
|
|
168
|
+
this.logWarning('Configuration missing environment, setting to development');
|
|
169
|
+
this.config.environment = 'development';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Validate paths
|
|
173
|
+
if (!this.config.paths) {
|
|
174
|
+
this.config.paths = this.getDefaultConfig().paths;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Ensure required directories exist
|
|
178
|
+
this.ensureDirectories();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Ensure required directories exist
|
|
183
|
+
*/
|
|
184
|
+
private ensureDirectories(): void {
|
|
185
|
+
if (!this.config?.paths) return;
|
|
186
|
+
|
|
187
|
+
const requiredDirs = [
|
|
188
|
+
this.config.paths.reports,
|
|
189
|
+
this.config.paths.coverage,
|
|
190
|
+
this.config.paths.artifacts,
|
|
191
|
+
path.dirname(this.config.logging?.file || ''),
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
for (const dir of requiredDirs) {
|
|
195
|
+
if (dir) {
|
|
196
|
+
this.ensureDirectoryExists(dir);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get current configuration
|
|
203
|
+
*/
|
|
204
|
+
getConfig(): CawsConfig {
|
|
205
|
+
return this.config || this.getDefaultConfig();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Update configuration
|
|
210
|
+
*/
|
|
211
|
+
updateConfig(updates: Partial<CawsConfig>): ToolResult {
|
|
212
|
+
try {
|
|
213
|
+
if (!this.config) {
|
|
214
|
+
this.config = this.getDefaultConfig();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Deep merge updates
|
|
218
|
+
this.config = {
|
|
219
|
+
...this.config,
|
|
220
|
+
...updates,
|
|
221
|
+
gates: { ...this.config.gates, ...updates.gates },
|
|
222
|
+
tools: { ...this.config.tools, ...updates.tools },
|
|
223
|
+
paths: { ...this.config.paths, ...updates.paths },
|
|
224
|
+
logging: { ...this.config.logging, ...updates.logging },
|
|
225
|
+
features: { ...this.config.features, ...updates.features },
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Validate and save
|
|
229
|
+
this.validateConfig();
|
|
230
|
+
|
|
231
|
+
if (this.saveConfig()) {
|
|
232
|
+
return this.createResult(true, 'Configuration updated successfully');
|
|
233
|
+
} else {
|
|
234
|
+
return this.createResult(false, 'Failed to save configuration');
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return this.createResult(false, `Failed to update configuration: ${error}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get specific configuration section
|
|
243
|
+
*/
|
|
244
|
+
getSection<K extends keyof CawsConfig>(section: K): CawsConfig[K] | null {
|
|
245
|
+
const config = this.getConfig();
|
|
246
|
+
return config[section] || null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get gate configuration
|
|
251
|
+
*/
|
|
252
|
+
getGateConfig(gateName: string): any {
|
|
253
|
+
const gates = this.getSection('gates');
|
|
254
|
+
return gates?.[gateName] || null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get tool configuration
|
|
259
|
+
*/
|
|
260
|
+
getToolConfig(toolName: string): any {
|
|
261
|
+
const tools = this.getSection('tools');
|
|
262
|
+
return tools?.[toolName] || null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get path configuration
|
|
267
|
+
*/
|
|
268
|
+
getPathConfig(pathName: string): string | null {
|
|
269
|
+
const paths = this.getSection('paths');
|
|
270
|
+
return paths?.[pathName] || null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Check if a feature is enabled
|
|
275
|
+
*/
|
|
276
|
+
isFeatureEnabled(feature: string): boolean {
|
|
277
|
+
const features = this.getSection('features');
|
|
278
|
+
const featureValue = features?.[feature];
|
|
279
|
+
return typeof featureValue === 'boolean' ? featureValue : featureValue?.enabled === true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get logging configuration
|
|
284
|
+
*/
|
|
285
|
+
getLoggingConfig() {
|
|
286
|
+
return this.getSection('logging');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Load configuration from file path
|
|
291
|
+
*/
|
|
292
|
+
loadConfigFromFile(filePath: string): ToolResult {
|
|
293
|
+
try {
|
|
294
|
+
const configData = this.readJsonFile(filePath);
|
|
295
|
+
if (!configData) {
|
|
296
|
+
return this.createResult(false, `Failed to read configuration from ${filePath}`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.config = configData as CawsConfig;
|
|
300
|
+
this.validateConfig();
|
|
301
|
+
|
|
302
|
+
return this.createResult(true, 'Configuration loaded from file');
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return this.createResult(false, `Failed to load configuration: ${error}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Save configuration to custom path
|
|
310
|
+
*/
|
|
311
|
+
saveConfigToFile(filePath: string): ToolResult {
|
|
312
|
+
try {
|
|
313
|
+
const saved = this.writeJsonFile(filePath, this.config);
|
|
314
|
+
if (saved) {
|
|
315
|
+
return this.createResult(true, `Configuration saved to ${filePath}`);
|
|
316
|
+
} else {
|
|
317
|
+
return this.createResult(false, `Failed to save configuration to ${filePath}`);
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return this.createResult(false, `Failed to save configuration: ${error}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Reset configuration to defaults
|
|
326
|
+
*/
|
|
327
|
+
resetConfig(): ToolResult {
|
|
328
|
+
this.config = this.getDefaultConfig();
|
|
329
|
+
return this.updateConfig({});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Export configuration as YAML
|
|
334
|
+
*/
|
|
335
|
+
exportAsYaml(): string | null {
|
|
336
|
+
try {
|
|
337
|
+
return yaml.dump(this.getConfig(), {
|
|
338
|
+
indent: 2,
|
|
339
|
+
lineWidth: 80,
|
|
340
|
+
noRefs: true,
|
|
341
|
+
});
|
|
342
|
+
} catch (error) {
|
|
343
|
+
this.logError(`Failed to export configuration as YAML: ${error}`);
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Import configuration from YAML
|
|
350
|
+
*/
|
|
351
|
+
importFromYaml(yamlContent: string): ToolResult {
|
|
352
|
+
try {
|
|
353
|
+
const configData = yaml.load(yamlContent) as CawsConfig;
|
|
354
|
+
this.config = configData;
|
|
355
|
+
this.validateConfig();
|
|
356
|
+
|
|
357
|
+
if (this.saveConfig()) {
|
|
358
|
+
return this.createResult(true, 'Configuration imported from YAML successfully');
|
|
359
|
+
} else {
|
|
360
|
+
return this.createResult(false, 'Failed to save imported configuration');
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
return this.createResult(false, `Failed to import configuration from YAML: ${error}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|