@paths.design/caws-cli 3.3.1 → 3.5.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 (43) hide show
  1. package/dist/commands/diagnose.d.ts.map +1 -1
  2. package/dist/commands/diagnose.js +39 -4
  3. package/dist/commands/evaluate.d.ts +8 -0
  4. package/dist/commands/evaluate.d.ts.map +1 -0
  5. package/dist/commands/evaluate.js +288 -0
  6. package/dist/commands/iterate.d.ts +8 -0
  7. package/dist/commands/iterate.d.ts.map +1 -0
  8. package/dist/commands/iterate.js +341 -0
  9. package/dist/commands/quality-monitor.d.ts +17 -0
  10. package/dist/commands/quality-monitor.d.ts.map +1 -0
  11. package/dist/commands/quality-monitor.js +265 -0
  12. package/dist/commands/status.d.ts +6 -1
  13. package/dist/commands/status.d.ts.map +1 -1
  14. package/dist/commands/status.js +120 -20
  15. package/dist/commands/troubleshoot.d.ts +8 -0
  16. package/dist/commands/troubleshoot.d.ts.map +1 -0
  17. package/dist/commands/troubleshoot.js +104 -0
  18. package/dist/commands/waivers.d.ts +8 -0
  19. package/dist/commands/waivers.d.ts.map +1 -0
  20. package/dist/commands/waivers.js +293 -0
  21. package/dist/commands/workflow.d.ts +85 -0
  22. package/dist/commands/workflow.d.ts.map +1 -0
  23. package/dist/commands/workflow.js +243 -0
  24. package/dist/error-handler.d.ts +91 -2
  25. package/dist/error-handler.d.ts.map +1 -1
  26. package/dist/error-handler.js +362 -16
  27. package/dist/index.js +95 -0
  28. package/dist/scaffold/git-hooks.d.ts.map +1 -1
  29. package/dist/scaffold/git-hooks.js +27 -6
  30. package/dist/utils/typescript-detector.d.ts +31 -0
  31. package/dist/utils/typescript-detector.d.ts.map +1 -1
  32. package/dist/utils/typescript-detector.js +245 -7
  33. package/package.json +2 -1
  34. package/templates/agents.md +6 -5
  35. package/templates/apps/tools/caws/gates.ts +34 -0
  36. package/templates/apps/tools/caws/shared/gate-checker.ts +265 -13
  37. package/templates/apps/tools/caws/templates/working-spec.template.yml +14 -0
  38. package/dist/index-new.d.ts +0 -5
  39. package/dist/index-new.d.ts.map +0 -1
  40. package/dist/index-new.js +0 -317
  41. package/dist/index.js.backup +0 -4711
  42. package/templates/apps/tools/caws/prompt-lint.js.backup +0 -274
  43. package/templates/apps/tools/caws/provenance.js.backup +0 -73
@@ -60,6 +60,146 @@ export class CawsGateChecker extends CawsBaseTool {
60
60
  }
61
61
  }
62
62
 
63
+ /**
64
+ * Auto-detect the correct working directory for coverage/mutation reports in monorepos
65
+ */
66
+ private findReportDirectory(startPath: string = this.getWorkingDirectory()): string {
67
+ // Priority 1: Check if the current directory has the reports or test results
68
+ if (
69
+ this.hasCoverageReports(startPath) ||
70
+ this.hasMutationReports(startPath) ||
71
+ this.hasTestResults(startPath)
72
+ ) {
73
+ return startPath;
74
+ }
75
+
76
+ // Priority 2: Check for npm workspaces configuration
77
+ const packageJsonPath = path.join(startPath, 'package.json');
78
+ if (this.pathExists(packageJsonPath)) {
79
+ try {
80
+ const packageJson = this.readJsonFile<any>(packageJsonPath);
81
+ if (packageJson?.workspaces) {
82
+ const workspaces = packageJson.workspaces;
83
+
84
+ // Handle workspace patterns (e.g., ["packages/*", "iterations/*"])
85
+ for (const wsPattern of workspaces) {
86
+ if (wsPattern.includes('*')) {
87
+ const baseDir = wsPattern.split('*')[0];
88
+ const fullBaseDir = path.join(startPath, baseDir);
89
+
90
+ if (this.pathExists(fullBaseDir)) {
91
+ const entries = fs.readdirSync(fullBaseDir, { withFileTypes: true });
92
+ for (const entry of entries) {
93
+ if (entry.isDirectory()) {
94
+ const wsPath = path.join(fullBaseDir, entry.name);
95
+ if (
96
+ this.hasCoverageReports(wsPath) ||
97
+ this.hasMutationReports(wsPath) ||
98
+ this.hasTestResults(wsPath)
99
+ ) {
100
+ return wsPath;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ } else {
106
+ // Direct workspace path
107
+ const wsPath = path.join(startPath, wsPattern);
108
+ if (
109
+ this.hasCoverageReports(wsPath) ||
110
+ this.hasMutationReports(wsPath) ||
111
+ this.hasTestResults(wsPath)
112
+ ) {
113
+ return wsPath;
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // Priority 3: If no reports found in workspaces, look for workspaces with test scripts
120
+ if (packageJson?.workspaces) {
121
+ for (const wsPattern of workspaces) {
122
+ if (wsPattern.includes('*')) {
123
+ const baseDir = wsPattern.split('*')[0];
124
+ const fullBaseDir = path.join(startPath, baseDir);
125
+
126
+ if (this.pathExists(fullBaseDir)) {
127
+ const entries = fs.readdirSync(fullBaseDir, { withFileTypes: true });
128
+ for (const entry of entries) {
129
+ if (entry.isDirectory()) {
130
+ const wsPath = path.join(fullBaseDir, entry.name);
131
+ if (this.hasTestScript(wsPath)) {
132
+ // Found a workspace with tests, prefer this even without reports
133
+ return wsPath;
134
+ }
135
+ }
136
+ }
137
+ }
138
+ } else {
139
+ const wsPath = path.join(startPath, wsPattern);
140
+ if (this.hasTestScript(wsPath)) {
141
+ return wsPath;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ } catch (error) {
147
+ // Ignore workspace parsing errors
148
+ }
149
+ }
150
+
151
+ // Fall back to original working directory
152
+ return startPath;
153
+ }
154
+
155
+ /**
156
+ * Check if a directory has coverage reports
157
+ */
158
+ private hasCoverageReports(dirPath: string): boolean {
159
+ const coveragePath = path.join(dirPath, 'coverage', 'coverage-final.json');
160
+ return this.pathExists(coveragePath);
161
+ }
162
+
163
+ /**
164
+ * Check if a directory has mutation reports
165
+ */
166
+ private hasMutationReports(dirPath: string): boolean {
167
+ const mutationPath = path.join(dirPath, 'reports', 'mutation', 'mutation.json');
168
+ return this.pathExists(mutationPath);
169
+ }
170
+
171
+ /**
172
+ * Check if a directory has test results
173
+ */
174
+ private hasTestResults(dirPath: string): boolean {
175
+ const testResultsPath = path.join(dirPath, 'test-results');
176
+ if (this.pathExists(testResultsPath)) {
177
+ try {
178
+ const entries = fs.readdirSync(testResultsPath);
179
+ return entries.some((entry) => entry.endsWith('.json') || entry.endsWith('.xml'));
180
+ } catch (error) {
181
+ // Ignore read errors
182
+ }
183
+ }
184
+ return false;
185
+ }
186
+
187
+ /**
188
+ * Check if a directory has a package.json with test scripts
189
+ */
190
+ private hasTestScript(dirPath: string): boolean {
191
+ const packageJsonPath = path.join(dirPath, 'package.json');
192
+ if (this.pathExists(packageJsonPath)) {
193
+ try {
194
+ const packageJson = this.readJsonFile<any>(packageJsonPath);
195
+ return !!packageJson?.scripts?.test;
196
+ } catch (error) {
197
+ // Ignore parse errors
198
+ }
199
+ }
200
+ return false;
201
+ }
202
+
63
203
  /**
64
204
  * Check if a waiver applies to the given gate
65
205
  */
@@ -223,10 +363,11 @@ export class CawsGateChecker extends CawsBaseTool {
223
363
  };
224
364
  }
225
365
 
226
- const coveragePath = path.join(
227
- options.workingDirectory || this.getWorkingDirectory(),
228
- 'coverage/coverage-final.json'
366
+ // Auto-detect the correct directory for coverage reports
367
+ const reportDir = this.findReportDirectory(
368
+ options.workingDirectory || this.getWorkingDirectory()
229
369
  );
370
+ const coveragePath = path.join(reportDir, 'coverage', 'coverage-final.json');
230
371
 
231
372
  if (!this.pathExists(coveragePath)) {
232
373
  return {
@@ -234,8 +375,55 @@ export class CawsGateChecker extends CawsBaseTool {
234
375
  score: 0,
235
376
  details: {
236
377
  error: 'Coverage report not found. Run tests with coverage first.',
378
+ searched_paths: [
379
+ path.join(reportDir, 'coverage', 'coverage-final.json'),
380
+ path.join(this.getWorkingDirectory(), 'coverage', 'coverage-final.json'),
381
+ ],
382
+ expected_format: 'Istanbul coverage format (coverage-final.json)',
383
+ expected_schema: {
384
+ description: 'JSON object with coverage data by file',
385
+ example: {
386
+ '/path/to/file.js': {
387
+ statementMap: {
388
+ /* ... */
389
+ },
390
+ fnMap: {
391
+ /* ... */
392
+ },
393
+ branchMap: {
394
+ /* ... */
395
+ },
396
+ s: {
397
+ /* hit counts */
398
+ },
399
+ f: {
400
+ /* function hits */
401
+ },
402
+ b: {
403
+ /* branch hits */
404
+ },
405
+ },
406
+ },
407
+ },
408
+ run_command: 'npm test -- --coverage --coverageReporters=json',
409
+ alternative_commands: [
410
+ 'npm run test:coverage',
411
+ 'jest --coverage --coverageReporters=json',
412
+ 'vitest run --coverage',
413
+ ],
414
+ workspace_hint:
415
+ reportDir !== this.getWorkingDirectory()
416
+ ? `Auto-detected workspace: ${path.relative(this.getWorkingDirectory(), reportDir)}`
417
+ : 'Run from workspace directory if using monorepo',
418
+ waiver_available: true,
419
+ waiver_suggestion:
420
+ 'If this is an exceptional case, consider creating a coverage waiver',
421
+ waiver_command:
422
+ 'caws waivers create --title="Coverage waiver" --reason=emergency_hotfix --gates=coverage',
237
423
  },
238
- errors: ['Coverage report not found'],
424
+ errors: [
425
+ `Coverage report not found at ${path.relative(this.getWorkingDirectory(), coveragePath)}`,
426
+ ],
239
427
  };
240
428
  }
241
429
 
@@ -356,10 +544,11 @@ export class CawsGateChecker extends CawsBaseTool {
356
544
  };
357
545
  }
358
546
 
359
- const mutationPath = path.join(
360
- options.workingDirectory || this.getWorkingDirectory(),
361
- 'reports/mutation/mutation.json'
547
+ // Auto-detect the correct directory for mutation reports
548
+ const reportDir = this.findReportDirectory(
549
+ options.workingDirectory || this.getWorkingDirectory()
362
550
  );
551
+ const mutationPath = path.join(reportDir, 'reports', 'mutation', 'mutation.json');
363
552
 
364
553
  if (!this.pathExists(mutationPath)) {
365
554
  return {
@@ -367,8 +556,49 @@ export class CawsGateChecker extends CawsBaseTool {
367
556
  score: 0,
368
557
  details: {
369
558
  error: 'Mutation report not found. Run mutation tests first.',
559
+ searched_paths: [
560
+ path.join(reportDir, 'reports', 'mutation', 'mutation.json'),
561
+ path.join(this.getWorkingDirectory(), 'reports', 'mutation', 'mutation.json'),
562
+ ],
563
+ expected_format: 'Stryker mutation testing JSON report',
564
+ expected_schema: {
565
+ description: 'JSON object with mutation testing results',
566
+ example: {
567
+ files: {
568
+ /* file-specific results */
569
+ },
570
+ testFiles: {
571
+ /* test file results */
572
+ },
573
+ mutants: [
574
+ {
575
+ /* mutant details */
576
+ },
577
+ ],
578
+ metrics: {
579
+ killed: 85,
580
+ survived: 5,
581
+ timeout: 2,
582
+ totalDetected: 92,
583
+ totalUndetected: 0,
584
+ totalValid: 92,
585
+ },
586
+ },
587
+ },
588
+ run_command: 'npx stryker run',
589
+ alternative_commands: [
590
+ 'npm run test:mutation',
591
+ 'npx stryker run --configFile stryker.conf.json',
592
+ 'yarn mutation:test',
593
+ ],
594
+ workspace_hint:
595
+ reportDir !== this.getWorkingDirectory()
596
+ ? `Auto-detected workspace: ${path.relative(this.getWorkingDirectory(), reportDir)}`
597
+ : 'Run from workspace directory if using monorepo',
370
598
  },
371
- errors: ['Mutation report not found'],
599
+ errors: [
600
+ `Mutation report not found at ${path.relative(this.getWorkingDirectory(), mutationPath)}`,
601
+ ],
372
602
  };
373
603
  }
374
604
 
@@ -439,17 +669,39 @@ export class CawsGateChecker extends CawsBaseTool {
439
669
  };
440
670
  }
441
671
 
442
- const contractResultsPath = path.join(
443
- options.workingDirectory || this.getWorkingDirectory(),
444
- 'test-results/contract-results.json'
672
+ // Auto-detect the correct directory for contract test results
673
+ const reportDir = this.findReportDirectory(
674
+ options.workingDirectory || this.getWorkingDirectory()
445
675
  );
676
+ const contractResultsPath = path.join(reportDir, 'test-results', 'contract-results.json');
446
677
 
447
678
  if (!this.pathExists(contractResultsPath)) {
448
679
  return {
449
680
  passed: false,
450
681
  score: 0,
451
- details: { error: 'Contract test results not found' },
452
- errors: ['Contract tests not run or results not found'],
682
+ details: {
683
+ error: 'Contract test results not found',
684
+ searched_paths: [
685
+ path.join(reportDir, 'test-results', 'contract-results.json'),
686
+ path.join(this.getWorkingDirectory(), 'test-results', 'contract-results.json'),
687
+ path.join(reportDir, '.caws', 'contract-results.json'),
688
+ path.join(this.getWorkingDirectory(), '.caws', 'contract-results.json'),
689
+ ],
690
+ expected_format:
691
+ 'JSON with { tests: [], passed: boolean, numPassed: number, numTotal: number }',
692
+ example_command:
693
+ 'npm run test:contract -- --json --outputFile=test-results/contract-results.json',
694
+ },
695
+ errors: [
696
+ `Contract test results not found. Searched in: ${[
697
+ path.relative(
698
+ this.getWorkingDirectory(),
699
+ path.join(reportDir, 'test-results', 'contract-results.json')
700
+ ),
701
+ 'test-results/contract-results.json',
702
+ '.caws/contract-results.json',
703
+ ].join(', ')}`,
704
+ ],
453
705
  };
454
706
  }
455
707
 
@@ -15,10 +15,24 @@ acceptance:
15
15
  given: '{{GIVEN_CONDITION}}'
16
16
  when: '{{WHEN_ACTION}}'
17
17
  then: '{{THEN_OUTCOME}}'
18
+ status: pending # pending | in_progress | completed
19
+ # Optional: detailed progress tracking
20
+ # tests:
21
+ # written: 0
22
+ # passing: 0
23
+ # coverage: 0.0
24
+ # last_updated: '2025-10-09T14:30:00Z'
18
25
  - id: A2
19
26
  given: '{{GIVEN_CONDITION_2}}'
20
27
  when: '{{WHEN_ACTION_2}}'
21
28
  then: '{{THEN_OUTCOME_2}}'
29
+ status: pending # pending | in_progress | completed
30
+ # Optional: detailed progress tracking
31
+ # tests:
32
+ # written: 0
33
+ # passing: 0
34
+ # coverage: 0.0
35
+ # last_updated: '2025-10-09T14:30:00Z'
22
36
  non_functional:
23
37
  a11y:
24
38
  - '{{ACCESSIBILITY_REQUIREMENT}}'
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { generateWorkingSpec } from "./generators/working-spec";
3
- import { validateGeneratedSpec } from "./generators/working-spec";
4
- export { generateWorkingSpec, validateGeneratedSpec };
5
- //# sourceMappingURL=index-new.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-new.d.ts","sourceRoot":"","sources":["../src/index-new.js"],"names":[],"mappings":""}
package/dist/index-new.js DELETED
@@ -1,317 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @fileoverview CAWS CLI - Scaffolding tool for Coding Agent Workflow System
5
- * Provides commands to initialize new projects and scaffold existing ones with CAWS
6
- * @author @darianrosebrook
7
- */
8
-
9
- const { Command } = require('commander');
10
- const fs = require('fs-extra');
11
- const path = require('path');
12
- const yaml = require('js-yaml');
13
- const chalk = require('chalk');
14
-
15
- // Import configuration and utilities
16
- const {
17
- CLI_VERSION,
18
- initializeGlobalSetup,
19
- loadProvenanceTools,
20
- initializeLanguageSupport,
21
- } = require('./config');
22
-
23
- // Import command handlers
24
- const { initProject } = require('./commands/init');
25
-
26
- // Import scaffold functionality
27
- const { scaffoldProject, setScaffoldDependencies } = require('./scaffold');
28
-
29
- // Import validation functionality
30
- const { validateWorkingSpecWithSuggestions } = require('./validation/spec-validation');
31
-
32
- // Import finalization utilities
33
- const {
34
- finalizeProject,
35
- continueToSuccess,
36
- setFinalizationDependencies,
37
- } = require('./utils/finalization');
38
-
39
- // Import generators
40
- const { generateWorkingSpec, validateGeneratedSpec } = require('./generators/working-spec');
41
-
42
- // Import tool system
43
- const ToolLoader = require('./tool-loader');
44
- const ToolValidator = require('./tool-validator');
45
-
46
- // Initialize global configuration
47
- const program = new Command();
48
-
49
- // Initialize global state
50
- const cawsSetup = initializeGlobalSetup();
51
- const languageSupport = initializeLanguageSupport();
52
-
53
- // Set up dependencies for modules that need them
54
- setScaffoldDependencies({
55
- cawsSetup,
56
- loadProvenanceTools,
57
- });
58
-
59
- setFinalizationDependencies({
60
- languageSupport,
61
- loadProvenanceTools,
62
- });
63
-
64
- // Tool system state
65
- let toolLoader = null;
66
- let toolValidator = null;
67
-
68
- /**
69
- * Initialize tool system
70
- */
71
- async function initializeToolSystem() {
72
- if (toolLoader) return toolLoader;
73
-
74
- try {
75
- toolLoader = new ToolLoader({
76
- toolsDir: path.join(process.cwd(), 'apps/tools/caws'),
77
- });
78
-
79
- toolValidator = new ToolValidator();
80
-
81
- // Set up event listeners for tool system
82
- toolLoader.on('discovery:complete', ({ tools: _tools, count }) => {
83
- if (count > 0) {
84
- console.log(chalk.blue(`🔧 Discovered ${count} tools`));
85
- }
86
- });
87
-
88
- toolLoader.on('tool:loaded', ({ id, metadata }) => {
89
- console.log(chalk.gray(` ✓ Loaded tool: ${metadata.name} (${id})`));
90
- });
91
-
92
- toolLoader.on('tool:error', ({ id, error }) => {
93
- console.warn(chalk.yellow(`⚠️ Failed to load tool ${id}: ${error}`));
94
- });
95
-
96
- // Auto-discover tools on initialization
97
- await toolLoader.discoverTools();
98
-
99
- return toolLoader;
100
- } catch (error) {
101
- console.warn(chalk.yellow('⚠️ Tool system initialization failed:'), error.message);
102
- console.warn(chalk.blue('💡 Continuing without dynamic tools'));
103
- return null;
104
- }
105
- }
106
-
107
- /**
108
- * Validate command handler
109
- */
110
- async function validateCommand(specFile, options) {
111
- try {
112
- let specPath = specFile || path.join('.caws', 'working-spec.yaml');
113
-
114
- if (!fs.existsSync(specPath)) {
115
- console.error(chalk.red(`❌ Spec file not found: ${specPath}`));
116
- console.error(chalk.blue('💡 Run "caws init" first to create a working spec'));
117
- process.exit(1);
118
- }
119
-
120
- const specContent = fs.readFileSync(specPath, 'utf8');
121
- const spec = yaml.load(specContent);
122
-
123
- console.log(chalk.cyan('🔍 Validating CAWS working spec...'));
124
-
125
- const result = validateWorkingSpecWithSuggestions(spec, {
126
- autoFix: options.autoFix,
127
- suggestions: !options.quiet,
128
- });
129
-
130
- if (result.valid) {
131
- console.log(chalk.green('✅ Working spec validation passed'));
132
- if (!options.quiet) {
133
- console.log(chalk.gray(` Risk tier: ${spec.risk_tier}`));
134
- console.log(chalk.gray(` Mode: ${spec.mode}`));
135
- if (spec.title) {
136
- console.log(chalk.gray(` Title: ${spec.title}`));
137
- }
138
- }
139
- } else {
140
- console.log(chalk.red('❌ Working spec validation failed'));
141
-
142
- // Show errors
143
- result.errors.forEach((error, index) => {
144
- console.log(` ${index + 1}. ${chalk.red(error.message)}`);
145
- if (error.suggestion) {
146
- console.log(` ${chalk.blue('💡 ' + error.suggestion)}`);
147
- }
148
- });
149
-
150
- // Show warnings
151
- if (result.warnings && result.warnings.length > 0) {
152
- console.log(chalk.yellow('\n⚠️ Warnings:'));
153
- result.warnings.forEach((warning, index) => {
154
- console.log(` ${index + 1}. ${chalk.yellow(warning.message)}`);
155
- });
156
- }
157
-
158
- process.exit(1);
159
- }
160
- } catch (error) {
161
- console.error(chalk.red('❌ Error during validation:'), error.message);
162
- process.exit(1);
163
- }
164
- }
165
-
166
- /**
167
- * Tool execution command handler
168
- */
169
- async function executeTool(toolId, options) {
170
- try {
171
- // Initialize tool system
172
- const loader = await initializeToolSystem();
173
-
174
- if (!loader) {
175
- console.error(chalk.red('❌ Tool system not available'));
176
- process.exit(1);
177
- }
178
-
179
- // Load all tools first
180
- await loader.loadAllTools();
181
- const tool = loader.getTool(toolId);
182
-
183
- if (!tool) {
184
- console.error(chalk.red(`❌ Tool '${toolId}' not found`));
185
- console.log(chalk.blue('💡 Available tools:'));
186
- const tools = loader.getAllTools();
187
- for (const [id, t] of tools) {
188
- console.log(` - ${id}: ${t.metadata.name}`);
189
- }
190
- process.exit(1);
191
- }
192
-
193
- // Validate tool before execution
194
- const validation = await toolValidator.validateTool(tool);
195
- if (!validation.valid) {
196
- console.error(chalk.red('❌ Tool validation failed:'));
197
- validation.errors.forEach((error) => {
198
- console.error(` ${chalk.red('✗')} ${error}`);
199
- });
200
- process.exit(1);
201
- }
202
-
203
- // Parse parameters
204
- let params = {};
205
- if (options.params) {
206
- try {
207
- params = JSON.parse(options.params);
208
- } catch (error) {
209
- console.error(chalk.red('❌ Invalid JSON parameters:'), error.message);
210
- process.exit(1);
211
- }
212
- }
213
-
214
- console.log(chalk.blue(`🚀 Executing tool: ${tool.metadata.name}`));
215
-
216
- // Execute tool
217
- const result = await tool.module.execute(params, {
218
- workingDirectory: process.cwd(),
219
- timeout: options.timeout,
220
- });
221
-
222
- // Display results
223
- if (result.success) {
224
- console.log(chalk.green('✅ Tool execution successful'));
225
- if (result.output && typeof result.output === 'object') {
226
- console.log(chalk.gray('Output:'), JSON.stringify(result.output, null, 2));
227
- }
228
- } else {
229
- console.error(chalk.red('❌ Tool execution failed'));
230
- result.errors.forEach((error) => {
231
- console.error(` ${chalk.red('✗')} ${error}`);
232
- });
233
- process.exit(1);
234
- }
235
- } catch (error) {
236
- console.error(chalk.red(`❌ Error executing tool ${toolId}:`), error.message);
237
- process.exit(1);
238
- }
239
- }
240
-
241
- // Setup CLI program
242
- program.name('caws').description('CAWS - Coding Agent Workflow System CLI').version(CLI_VERSION);
243
-
244
- // Init command
245
- program
246
- .command('init')
247
- .description('Initialize a new project with CAWS')
248
- .argument('[project-name]', 'Name of the project to create (use "." for current directory)')
249
- .option('-i, --interactive', 'Run interactive setup wizard', true)
250
- .option('--non-interactive', 'Skip interactive prompts (use defaults)', false)
251
- .option('--template <template>', 'Use specific project template')
252
- .action(initProject);
253
-
254
- // Scaffold command
255
- program
256
- .command('scaffold')
257
- .description('Add CAWS components to existing project')
258
- .option('-f, --force', 'Overwrite existing files', false)
259
- .option('--minimal', 'Only essential components', false)
260
- .option('--with-codemods', 'Include codemod scripts', false)
261
- .option('--with-oidc', 'Include OIDC trusted publisher setup', false)
262
- .action(scaffoldProject);
263
-
264
- // Validate command
265
- program
266
- .command('validate')
267
- .description('Validate CAWS working spec with suggestions')
268
- .argument('[spec-file]', 'Path to working spec file (default: .caws/working-spec.yaml)')
269
- .option('-q, --quiet', 'Suppress suggestions and warnings', false)
270
- .option('--auto-fix', 'Automatically fix safe validation issues', false)
271
- .action(validateCommand);
272
-
273
- // Tool command
274
- program
275
- .command('tool')
276
- .description('Execute CAWS tools programmatically')
277
- .argument('<tool-id>', 'ID of the tool to execute')
278
- .option('-p, --params <json>', 'Parameters as JSON string', '{}')
279
- .option('-t, --timeout <ms>', 'Execution timeout in milliseconds', parseInt, 30000)
280
- .action(executeTool);
281
-
282
- // Error handling
283
- program.exitOverride((err) => {
284
- if (
285
- err.code === 'commander.help' ||
286
- err.code === 'commander.version' ||
287
- err.message.includes('outputHelp')
288
- ) {
289
- process.exit(0);
290
- }
291
- console.error(chalk.red('❌ Error:'), err.message);
292
- process.exit(1);
293
- });
294
-
295
- // Parse and run
296
- if (require.main === module) {
297
- try {
298
- program.parse();
299
- } catch (error) {
300
- if (
301
- error.code === 'commander.help' ||
302
- error.code === 'commander.version' ||
303
- error.message.includes('outputHelp')
304
- ) {
305
- process.exit(0);
306
- } else {
307
- console.error(chalk.red('❌ Error:'), error.message);
308
- process.exit(1);
309
- }
310
- }
311
- }
312
-
313
- // Export functions for testing
314
- module.exports = {
315
- generateWorkingSpec,
316
- validateGeneratedSpec,
317
- };