@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,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
+ }