@paths.design/caws-cli 5.0.1 → 5.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.
Files changed (115) hide show
  1. package/README.md +15 -12
  2. package/dist/budget-derivation.d.ts +74 -0
  3. package/dist/budget-derivation.d.ts.map +1 -0
  4. package/dist/cicd-optimizer.d.ts +142 -0
  5. package/dist/cicd-optimizer.d.ts.map +1 -0
  6. package/dist/commands/archive.d.ts +50 -0
  7. package/dist/commands/archive.d.ts.map +1 -0
  8. package/dist/commands/burnup.d.ts +6 -0
  9. package/dist/commands/burnup.d.ts.map +1 -0
  10. package/dist/commands/diagnose.d.ts +52 -0
  11. package/dist/commands/diagnose.d.ts.map +1 -0
  12. package/dist/commands/evaluate.d.ts +8 -0
  13. package/dist/commands/evaluate.d.ts.map +1 -0
  14. package/dist/commands/init.d.ts +5 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/iterate.d.ts +8 -0
  17. package/dist/commands/iterate.d.ts.map +1 -0
  18. package/dist/commands/mode.d.ts +24 -0
  19. package/dist/commands/mode.d.ts.map +1 -0
  20. package/dist/commands/plan.d.ts +49 -0
  21. package/dist/commands/plan.d.ts.map +1 -0
  22. package/dist/commands/provenance.d.ts +32 -0
  23. package/dist/commands/provenance.d.ts.map +1 -0
  24. package/dist/commands/provenance.js +27 -22
  25. package/dist/commands/quality-gates.d.ts +52 -0
  26. package/dist/commands/quality-gates.d.ts.map +1 -0
  27. package/dist/commands/quality-gates.js +190 -455
  28. package/dist/commands/quality-monitor.d.ts +17 -0
  29. package/dist/commands/quality-monitor.d.ts.map +1 -0
  30. package/dist/commands/specs.d.ts +71 -0
  31. package/dist/commands/specs.d.ts.map +1 -0
  32. package/dist/commands/specs.js +34 -35
  33. package/dist/commands/status.d.ts +44 -0
  34. package/dist/commands/status.d.ts.map +1 -0
  35. package/dist/commands/status.js +10 -7
  36. package/dist/commands/templates.d.ts +74 -0
  37. package/dist/commands/templates.d.ts.map +1 -0
  38. package/dist/commands/tool.d.ts +13 -0
  39. package/dist/commands/tool.d.ts.map +1 -0
  40. package/dist/commands/tool.js +63 -63
  41. package/dist/commands/troubleshoot.d.ts +8 -0
  42. package/dist/commands/troubleshoot.d.ts.map +1 -0
  43. package/dist/commands/tutorial.d.ts +55 -0
  44. package/dist/commands/tutorial.d.ts.map +1 -0
  45. package/dist/commands/validate.d.ts +15 -0
  46. package/dist/commands/validate.d.ts.map +1 -0
  47. package/dist/commands/waivers.d.ts +8 -0
  48. package/dist/commands/waivers.d.ts.map +1 -0
  49. package/dist/commands/waivers.js +38 -39
  50. package/dist/commands/workflow.d.ts +85 -0
  51. package/dist/commands/workflow.d.ts.map +1 -0
  52. package/dist/config/index.d.ts +29 -0
  53. package/dist/config/index.d.ts.map +1 -0
  54. package/dist/config/modes.d.ts +225 -0
  55. package/dist/config/modes.d.ts.map +1 -0
  56. package/dist/constants/spec-types.d.ts +41 -0
  57. package/dist/constants/spec-types.d.ts.map +1 -0
  58. package/dist/error-handler.d.ts +164 -0
  59. package/dist/error-handler.d.ts.map +1 -0
  60. package/dist/generators/jest-config.d.ts +32 -0
  61. package/dist/generators/jest-config.d.ts.map +1 -0
  62. package/dist/generators/working-spec.d.ts +13 -0
  63. package/dist/generators/working-spec.d.ts.map +1 -0
  64. package/dist/index-new.d.ts +5 -0
  65. package/dist/index-new.d.ts.map +1 -0
  66. package/dist/index-new.js +317 -0
  67. package/dist/index.d.ts +5 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +1 -0
  70. package/dist/index.js.backup +4711 -0
  71. package/dist/minimal-cli.d.ts +3 -0
  72. package/dist/minimal-cli.d.ts.map +1 -0
  73. package/dist/policy/PolicyManager.d.ts +104 -0
  74. package/dist/policy/PolicyManager.d.ts.map +1 -0
  75. package/dist/policy/PolicyManager.js +60 -28
  76. package/dist/scaffold/cursor-hooks.d.ts +7 -0
  77. package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
  78. package/dist/scaffold/git-hooks.d.ts +20 -0
  79. package/dist/scaffold/git-hooks.d.ts.map +1 -0
  80. package/dist/scaffold/git-hooks.js +89 -27
  81. package/dist/scaffold/index.d.ts +20 -0
  82. package/dist/scaffold/index.d.ts.map +1 -0
  83. package/dist/scaffold/index.js +25 -0
  84. package/dist/spec/SpecFileManager.d.ts +146 -0
  85. package/dist/spec/SpecFileManager.d.ts.map +1 -0
  86. package/dist/test-analysis.d.ts +182 -0
  87. package/dist/test-analysis.d.ts.map +1 -0
  88. package/dist/tool-interface.d.ts +236 -0
  89. package/dist/tool-interface.d.ts.map +1 -0
  90. package/dist/tool-loader.d.ts +77 -0
  91. package/dist/tool-loader.d.ts.map +1 -0
  92. package/dist/tool-validator.d.ts +72 -0
  93. package/dist/tool-validator.d.ts.map +1 -0
  94. package/dist/utils/async-utils.js +188 -0
  95. package/dist/utils/command-wrapper.js +200 -0
  96. package/dist/utils/detection.d.ts +7 -0
  97. package/dist/utils/detection.d.ts.map +1 -0
  98. package/dist/utils/finalization.d.ts +17 -0
  99. package/dist/utils/finalization.d.ts.map +1 -0
  100. package/dist/utils/project-analysis.d.ts +14 -0
  101. package/dist/utils/project-analysis.d.ts.map +1 -0
  102. package/dist/utils/promise-utils.js +72 -0
  103. package/dist/utils/quality-gates.d.ts +49 -0
  104. package/dist/utils/quality-gates.d.ts.map +1 -0
  105. package/dist/utils/spec-resolver.d.ts +88 -0
  106. package/dist/utils/spec-resolver.d.ts.map +1 -0
  107. package/dist/utils/typescript-detector.d.ts +63 -0
  108. package/dist/utils/typescript-detector.d.ts.map +1 -0
  109. package/dist/validation/spec-validation.d.ts +43 -0
  110. package/dist/validation/spec-validation.d.ts.map +1 -0
  111. package/dist/waivers-manager.d.ts +167 -0
  112. package/dist/waivers-manager.d.ts.map +1 -0
  113. package/package.json +1 -1
  114. package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
  115. package/templates/apps/tools/caws/provenance.js.backup +73 -0
@@ -11,478 +11,213 @@
11
11
  * @author @darianrosebrook
12
12
  */
13
13
 
14
- const chalk = require('chalk');
15
14
  const fs = require('fs');
16
15
  const path = require('path');
17
16
  const { spawn } = require('child_process');
18
- // const { execSync } = require('child_process');
19
- // const crypto = require('crypto');
20
- // const yaml = require('js-yaml');
17
+ const { commandWrapper, Output } = require('../utils/command-wrapper');
18
+ const { withTimeout } = require('../utils/async-utils');
21
19
 
22
- /**
23
- * Quality Gates Configuration
24
- */
25
- // const QUALITY_CONFIG = {
26
- // godObjectThresholds: {
27
- // warning: 1750, // Lines of code
28
- // critical: 2000,
29
- // },
30
- // todoConfidenceThreshold: 0.8,
31
- // cawsTierThresholds: {
32
- // 1: { coverage: 90, mutation: 70, contracts: true, review: true },
33
- // 2: { coverage: 80, mutation: 50, contracts: true, review: false },
34
- // 3: { coverage: 70, mutation: 30, contracts: false, review: false },
35
- // },
36
- // crisisResponseThresholds: {
37
- // godObjectCritical: 3000, // Higher threshold in crisis mode
38
- // todoConfidenceThreshold: 0.9, // Stricter TODO detection
39
- // },
40
- // };
41
-
42
- /**
43
- * Update provenance with quality gates results
44
- * @param {Object} results - Quality gates results
45
- * @param {boolean} crisisMode - Whether in crisis mode
46
- * @param {string[]} stagedFiles - Array of staged files
47
- */
48
- // NOTE: updateProvenance function commented out to avoid lint errors
49
-
50
- /**
51
- * Detect agent type for provenance tracking
52
- * @returns {string} Agent type identifier
53
- */
54
- // function detectAgentType() {
55
- // try {
56
- // // Check for Cursor IDE indicators
57
- // if (process.env.CURSOR_USER_DATA_DIR) {
58
- // return 'cursor-ide';
59
- // }
60
-
61
- // // Check for VS Code indicators
62
- // if (process.env.VSCODE_PID) {
63
- // return 'vscode';
64
- // }
65
-
66
- // // Check for GitHub Copilot indicators
67
- // if (process.env.GITHUB_COPILOT_ENABLED) {
68
- // return 'github-copilot';
69
- // }
70
-
71
- // // Check for command line usage
72
- // if (process.env.TERM) {
73
- // return 'cli';
74
- // }
75
-
76
- // return 'unknown';
77
- // } catch (error) {
78
- // return 'unknown';
79
- // }
80
- // }
81
-
82
- /**
83
- * Check if a waiver applies to the given gate
84
- * @param {string} gate - Gate name to check
85
- * @returns {Object} Waiver check result
86
- */
87
- // function checkWaiver(gate) {
88
- // try {
89
- // const waiversPath = path.join(process.cwd(), '.caws/waivers.yml');
90
- // if (!fs.existsSync(waiversPath)) {
91
- // return { waived: false, reason: 'No waivers file found' };
92
- // }
93
-
94
- // const waiversConfig = yaml.load(fs.readFileSync(waiversPath, 'utf8'));
95
- // const now = new Date();
96
-
97
- // // Find active waivers for this gate
98
- // const activeWaivers =
99
- // waiversConfig.waivers?.filter((waiver) => {
100
- // const expiresAt = new Date(waiver.expires_at);
101
- // return waiver.gates.includes(gate) && expiresAt > now && waiver.status === 'active';
102
- // }) || [];
103
-
104
- // if (activeWaivers.length > 0) {
105
- // const waiver = activeWaivers[0]; // Use first active waiver
106
- // return {
107
- // waived: true,
108
- // waiver,
109
- // reason: `Active waiver: ${waiver.title} (expires: ${waiver.expires_at})`,
110
- // };
111
- // }
112
-
113
- // return { waived: false, reason: 'No active waivers found' };
114
- // } catch (error) {
115
- // return { waived: false, reason: `Waiver check failed: ${error.message}` };
116
- // }
117
- // }
118
-
119
- /**
120
- * Detect if project is in crisis response mode
121
- * @returns {boolean} True if in crisis mode
122
- */
123
- // function detectCrisisMode() {
124
- // try {
125
- // // Check for crisis indicators
126
- // const crisisIndicators = [
127
- // // Check for crisis response in working spec
128
- // () => {
129
- // const specPath = path.join(process.cwd(), '.caws/working-spec.yaml');
130
- // if (fs.existsSync(specPath)) {
131
- // const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
132
- // return spec.mode === 'crisis' || spec.crisis_mode === true;
133
- // }
134
- // return false;
135
- // },
136
- // // Check for crisis response in environment
137
- // () => process.env.CAWS_CRISIS_MODE === 'true',
138
- // // Check for crisis response in git commit message
139
- // () => {
140
- // try {
141
- // const lastCommit = execSync('git log -1 --pretty=%B', { encoding: 'utf8' });
142
- // return (
143
- // lastCommit.toLowerCase().includes('crisis') ||
144
- // lastCommit.toLowerCase().includes('emergency')
145
- // );
146
- // } catch {
147
- // return false;
148
- // }
149
- // },
150
- // ];
151
-
152
- // return crisisIndicators.some((indicator) => indicator());
153
- // } catch (error) {
154
- // return false;
155
- // }
156
- // }
157
-
158
- /**
159
- * Get staged files from git
160
- * @returns {string[]} Array of staged file paths
161
- */
162
- // function getStagedFiles() {
163
- // try {
164
- // const stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf8' })
165
- // .trim()
166
- // .split('\n')
167
- // .filter((file) => file.trim() !== '');
168
-
169
- // return stagedFiles;
170
- // } catch (error) {
171
- // console.warn(chalk.yellow(`⚠️ Could not get staged files: ${error.message}`));
172
- // return [];
173
- // }
174
- // }
175
-
176
- /**
177
- * Check for god objects in staged Rust files with waiver and crisis mode support
178
- * @param {string[]} stagedFiles - Array of staged file paths
179
- * @param {boolean} crisisMode - Whether in crisis response mode
180
- * @returns {Object} God object analysis results
181
- */
182
- // function checkGodObjects(stagedFiles, crisisMode = false) {
183
- // const rustFiles = stagedFiles.filter((file) => file.endsWith('.rs'));
184
-
185
- // if (rustFiles.length === 0) {
186
- // return { violations: [], warnings: [], total: 0, errors: [] };
187
- // }
188
-
189
- // console.log(chalk.blue(`📁 Found ${rustFiles.length} staged Rust files to check`));
190
-
191
- // // Check for god object waiver
192
- // const waiverCheck = checkWaiver('god_objects');
193
- // if (waiverCheck.waived) {
194
- // console.log(chalk.yellow(`⚠️ God object check waived: ${waiverCheck.reason}`));
195
- // return { violations: [], warnings: [], total: 0, waived: true, errors: [] };
196
- // }
197
-
198
- // const violations = [];
199
- // const warnings = [];
200
- // const errors = [];
201
-
202
- // // Use crisis mode thresholds if in crisis
203
- // const thresholds = crisisMode
204
- // ? {
205
- // warning: QUALITY_CONFIG.godObjectThresholds.warning,
206
- // critical: QUALITY_CONFIG.crisisResponseThresholds.godObjectCritical,
207
- // }
208
- // : QUALITY_CONFIG.godObjectThresholds;
209
-
210
- // for (const file of rustFiles) {
211
- // try {
212
- // const fullPath = path.resolve(file);
213
- // if (!fs.existsSync(fullPath)) continue;
214
-
215
- // const content = fs.readFileSync(fullPath, 'utf8');
216
- // const lineCount = content.split('\n').length;
217
- // const fileSizeKB = fs.statSync(fullPath).size / 1024;
218
-
219
- // if (lineCount >= thresholds.critical) {
220
- // const error = createGodObjectError(file, lineCount, thresholds.critical, {
221
- // fileSizeKB,
222
- // relativePath: file,
223
- // crisisMode,
224
- // });
225
-
226
- // violations.push({
227
- // file,
228
- // lines: lineCount,
229
- // severity: 'critical',
230
- // message: `CRITICAL: ${lineCount} LOC exceeds god object threshold (${thresholds.critical}+ LOC)${crisisMode ? ' [CRISIS MODE]' : ''}`,
231
- // error: error.toJSON(),
232
- // });
233
-
234
- // errors.push(error);
235
- // } else if (lineCount >= thresholds.warning) {
236
- // const error = createGodObjectError(file, lineCount, thresholds.warning, {
237
- // fileSizeKB,
238
- // relativePath: file,
239
- // crisisMode,
240
- // });
241
-
242
- // warnings.push({
243
- // file,
244
- // lines: lineCount,
245
- // severity: 'warning',
246
- // message: `WARNING: ${lineCount} LOC approaches god object territory (${thresholds.warning}+ LOC)${crisisMode ? ' [CRISIS MODE]' : ''}`,
247
- // error: error.toJSON(),
248
- // });
249
-
250
- // errors.push(error);
251
- // }
252
- // } catch (error) {
253
- // const fsError = createFileSystemError('read_file', file, error, {
254
- // operation: 'check_god_objects',
255
- // });
256
- // errors.push(fsError);
257
- // console.warn(chalk.yellow(`⚠️ Could not analyze ${file}: ${error.message}`));
258
- // }
259
- // }
260
-
261
- // return { violations, warnings, total: violations.length + warnings.length, errors };
262
- // }
263
-
264
- /**
265
- * Check for hidden TODOs in staged files with waiver and crisis mode support
266
- * @param {string[]} stagedFiles - Array of staged file paths
267
- * @param {boolean} crisisMode - Whether in crisis response mode
268
- * @returns {Object} TODO analysis results
269
- */
270
- // function checkHiddenTodos(stagedFiles, crisisMode = false) {
271
- // const supportedFiles = stagedFiles.filter((file) => /\.(rs|ts|tsx|js|jsx|py)$/.test(file));
272
-
273
- // if (supportedFiles.length === 0) {
274
- // return { todos: [], blocking: 0, total: 0, errors: [] };
275
- // }
276
-
277
- // console.log(chalk.blue(`📁 Found ${supportedFiles.length} staged files to analyze for TODOs`));
278
-
279
- // // Check for TODO waiver
280
- // const waiverCheck = checkWaiver('hidden_todos');
281
- // if (waiverCheck.waived) {
282
- // console.log(chalk.yellow(`⚠️ Hidden TODO check waived: ${waiverCheck.reason}`));
283
- // return { todos: [], blocking: 0, total: 0, waived: true, errors: [] };
284
- // }
285
-
286
- // try {
287
- // // Use crisis mode confidence threshold if in crisis
288
- // const confidenceThreshold = crisisMode
289
- // ? QUALITY_CONFIG.crisisResponseThresholds.todoConfidenceThreshold
290
- // : QUALITY_CONFIG.todoConfidenceThreshold;
291
-
292
- // // Run the TODO analyzer with staged files
293
- // const result = execSync(
294
- // `python3 scripts/v3/analysis/todo_analyzer.py --staged-only --min-confidence ${confidenceThreshold}`,
295
- // { encoding: 'utf8', cwd: process.cwd() }
296
- // );
297
-
298
- // // Parse the output to extract TODO count
299
- // const lines = result.split('\n');
300
- // const summaryLine = lines.find((line) => line.includes('Total hidden TODOs:'));
301
- // const todoCount = summaryLine ? parseInt(summaryLine.split(':')[1].trim()) : 0;
302
-
303
- // const errors = [];
304
- // if (todoCount > 0) {
305
- // // Create error for each file with TODOs (simplified for now)
306
- // const error = createHiddenTodoError('staged_files', todoCount, confidenceThreshold, {
307
- // crisisMode,
308
- // analyzerOutput: result,
309
- // confidenceThreshold,
310
- // });
311
- // errors.push(error);
312
- // }
313
-
314
- // return {
315
- // todos: [],
316
- // blocking: todoCount,
317
- // total: todoCount,
318
- // details: result,
319
- // crisisMode,
320
- // errors,
321
- // };
322
- // } catch (error) {
323
- // const execError = createExecutionError(
324
- // 'python3 scripts/v3/analysis/todo_analyzer.py',
325
- // error.status || 1,
326
- // error.stderr || error.message,
327
- // {
328
- // stdout: error.stdout,
329
- // workingDirectory: process.cwd(),
330
- // }
331
- // );
332
-
333
- // console.warn(chalk.yellow(`⚠️ Could not run TODO analysis: ${error.message}`));
334
- // return { todos: [], blocking: 0, total: 0, errors: [execError] };
335
- // }
336
- // }
337
-
338
- /**
339
- * Check for human override in working spec
340
- * @returns {Object} Human override check result
341
- */
342
- // function checkHumanOverride() {
343
- // try {
344
- // const specPath = path.join(process.cwd(), '.caws/working-spec.yaml');
345
- // if (!fs.existsSync(specPath)) {
346
- // return { override: false, reason: 'No working spec found' };
347
- // }
348
-
349
- // const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
350
- // const humanOverride = spec.human_override;
351
-
352
- // if (humanOverride && humanOverride.active) {
353
- // return {
354
- // override: true,
355
- // reason: humanOverride.reason || 'Human override active',
356
- // timestamp: humanOverride.timestamp,
357
- // approver: humanOverride.approver,
358
- // };
359
- // }
360
-
361
- // return { override: false, reason: 'No human override found' };
362
- // } catch (error) {
363
- // return { override: false, reason: `Override check failed: ${error.message}` };
364
- // }
365
- // }
366
-
367
- /**
368
- * Get CAWS tier from working spec
369
- * @returns {number|null} CAWS tier (1, 2, or 3) or null if not found
370
- */
371
- // function getCawsTier() {
372
- // try {
373
- // const specPath = path.join(process.cwd(), '.caws/working-spec.yaml');
374
- // if (!fs.existsSync(specPath)) return null;
375
-
376
- // const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
377
- // return spec.risk_tier || null;
378
- // } catch (error) {
379
- // return null;
380
- // }
381
- // }
20
+ // Quality gates runner implementation - delegates to external package
382
21
 
383
22
  /**
384
23
  * Run comprehensive quality gates on staged files
385
24
  * @param {Object} options - Command options
386
25
  */
387
26
  async function qualityGatesCommand(options = {}) {
388
- try {
389
- console.log(chalk.bold('\n🚦 CAWS Quality Gates - Enterprise Code Quality Enforcement'));
390
- console.log('='.repeat(70));
391
-
392
- // Find the quality gates runner script
393
- const cliSrcDir = path.dirname(__filename); // packages/caws-cli/src/commands -> packages/caws-cli/src/commands
394
- const cliSrcRoot = path.dirname(cliSrcDir); // packages/caws-cli/src/commands -> packages/caws-cli/src
395
- const cliPackageDir = path.dirname(cliSrcRoot); // packages/caws-cli/src -> packages/caws-cli
396
- const packagesDir = path.dirname(cliPackageDir); // packages/caws-cli -> packages
397
- const qualityGatesRunner = path.join(packagesDir, 'quality-gates', 'run-quality-gates.mjs');
398
-
399
- // Check if the runner exists
400
- if (!fs.existsSync(qualityGatesRunner)) {
401
- console.error(chalk.red('❌ Quality gates runner not found at:'));
402
- console.error(chalk.gray(` ${qualityGatesRunner}`));
403
- console.error(chalk.yellow('💡 Run from project root or ensure quality gates are installed'));
404
- process.exit(1);
405
- }
406
-
407
- // Build command arguments
408
- const args = ['node', qualityGatesRunner];
409
-
410
- // Map CLI options to runner options
411
- if (options.ci) {
412
- args.push('--ci');
413
- }
414
-
415
- if (options.json) {
416
- args.push('--json');
417
- }
418
-
419
- if (options.gates && options.gates.trim()) {
420
- args.push('--gates', options.gates.trim());
421
- }
422
-
423
- if (options.fix) {
424
- args.push('--fix');
425
- }
426
-
427
- // Add CAWS-specific environment variables for integration
428
- const env = {
429
- ...process.env,
430
- CAWS_CLI_INTEGRATION: 'true',
431
- CAWS_CLI_VERSION: require(path.join(cliPackageDir, 'package.json')).version,
432
- };
433
-
434
- // Set GitHub Actions summary if available
435
- if (process.env.GITHUB_STEP_SUMMARY) {
436
- env.GITHUB_STEP_SUMMARY = process.env.GITHUB_STEP_SUMMARY;
437
- }
438
-
439
- console.log(chalk.blue('📁 Executing quality gates runner...'));
440
- console.log(chalk.gray(` Command: ${args.join(' ')}`));
441
-
442
- // Execute the quality gates runner
443
- const child = spawn(args[0], args.slice(1), {
444
- stdio: 'inherit',
445
- cwd: packagesDir,
446
- env: env,
447
- });
448
-
449
- // Wait for completion
450
- return new Promise((resolve, reject) => {
451
- child.on('close', (code) => {
452
- if (code === 0) {
453
- console.log(chalk.green('\n✅ Quality gates completed successfully'));
454
- resolve();
455
- } else {
456
- console.log(chalk.red(`\n❌ Quality gates failed with exit code: ${code}`));
27
+ return commandWrapper(
28
+ async () => {
29
+ Output.section('CAWS Quality Gates - Enterprise Code Quality Enforcement');
30
+
31
+ const projectRoot = process.cwd();
32
+ let qualityGatesRunner = null;
33
+
34
+ // Fallback chain for finding quality gates runner:
35
+ // 1. Check monorepo structure (current behavior)
36
+ // 2. Check node_modules for @paths.design/quality-gates package
37
+ // 3. Check project-local scripts
38
+ // 4. Provide helpful error with alternatives
39
+
40
+ // Option 1: Check monorepo structure
41
+ const cliSrcDir = path.dirname(__filename);
42
+ const cliSrcRoot = path.dirname(cliSrcDir);
43
+ const cliPackageDir = path.dirname(cliSrcRoot);
44
+ const packagesDir = path.dirname(cliPackageDir);
45
+ const monorepoRunner = path.join(packagesDir, 'quality-gates', 'run-quality-gates.mjs');
46
+
47
+ // Option 2: Check VS Code extension bundled (if running from extension context)
48
+ const vscodeExtensionPath =
49
+ process.env.VSCODE_EXTENSION_PATH || process.env.VSCODE_EXTENSION_DIR;
50
+ const bundledRunner = vscodeExtensionPath
51
+ ? path.join(vscodeExtensionPath, 'bundled', 'quality-gates', 'run-quality-gates.mjs')
52
+ : null;
53
+
54
+ // Option 3: Check node_modules for quality-gates package
55
+ const nodeModulesPaths = [
56
+ path.join(projectRoot, 'node_modules', '@caws', 'quality-gates', 'run-quality-gates.mjs'),
57
+ path.join(
58
+ projectRoot,
59
+ 'node_modules',
60
+ '@paths.design',
61
+ 'quality-gates',
62
+ 'run-quality-gates.mjs'
63
+ ),
64
+ path.join(projectRoot, 'node_modules', 'quality-gates', 'run-quality-gates.mjs'),
65
+ ];
66
+
67
+ // Try all possible paths in order
68
+ if (fs.existsSync(monorepoRunner)) {
69
+ qualityGatesRunner = monorepoRunner;
70
+ } else if (bundledRunner && fs.existsSync(bundledRunner)) {
71
+ qualityGatesRunner = bundledRunner;
72
+ } else {
73
+ for (const nmPath of nodeModulesPaths) {
74
+ if (fs.existsSync(nmPath)) {
75
+ qualityGatesRunner = nmPath;
76
+ break;
77
+ }
78
+ }
79
+ }
80
+
81
+ // Option 4: Check for project-local Python scripts
82
+ if (!qualityGatesRunner) {
83
+ const pythonScript = path.join(projectRoot, 'scripts', 'simple_gates.py');
84
+ const makefile = path.join(projectRoot, 'Makefile');
85
+
86
+ if (fs.existsSync(pythonScript)) {
87
+ Output.warning(
88
+ 'Node.js quality gates runner not found',
89
+ 'Found Python script - falling back to Python implementation'
90
+ );
91
+ Output.info(`Running: python3 ${pythonScript}`);
92
+ Output.info(
93
+ 'Tip: Install quality gates package for better integration: npm install -g @paths.design/quality-gates'
94
+ );
95
+
96
+ // Execute Python script instead
97
+ const { execSync } = require('child_process');
98
+ const pythonArgs = ['all', '--tier', '2', '--profile', 'backend-api'];
457
99
  if (options.ci) {
458
- process.exit(code);
100
+ pythonArgs.push('--ci');
101
+ }
102
+ if (options.json) {
103
+ pythonArgs.push('--json');
459
104
  }
460
- reject(new Error(`Quality gates failed with exit code: ${code}`));
105
+
106
+ execSync(`python3 ${pythonScript} ${pythonArgs.join(' ')}`, {
107
+ stdio: 'inherit',
108
+ cwd: projectRoot,
109
+ });
110
+ Output.success('Quality gates completed successfully');
111
+ return;
112
+ } else if (fs.existsSync(makefile)) {
113
+ Output.warning(
114
+ 'Node.js quality gates runner not found',
115
+ 'Found Makefile - falling back to Makefile target'
116
+ );
117
+ Output.info('Running: make caws-gates');
118
+ Output.info(
119
+ 'Tip: Install quality gates package for better integration: npm install -g @paths.design/quality-gates'
120
+ );
121
+
122
+ // Execute Makefile target
123
+ const { execSync } = require('child_process');
124
+ execSync('make caws-gates', {
125
+ stdio: 'inherit',
126
+ cwd: projectRoot,
127
+ });
128
+ Output.success('Quality gates completed successfully');
129
+ return;
461
130
  }
131
+ }
132
+
133
+ // If still no runner found, provide helpful error
134
+ if (!qualityGatesRunner) {
135
+ const suggestions = [
136
+ 'Install quality gates package: npm install -g @paths.design/quality-gates',
137
+ 'Use Python script (if available): python3 scripts/simple_gates.py all --tier 2 --profile backend-api',
138
+ 'Use Makefile target (if available): make caws-gates',
139
+ 'Run from CAWS monorepo root',
140
+ ];
141
+
142
+ throw new Error(
143
+ 'Quality gates runner not found.\n\n' +
144
+ 'Expected locations:\n' +
145
+ ` • Monorepo: ${monorepoRunner}\n` +
146
+ ` • npm package: ${path.join(projectRoot, 'node_modules', '@paths.design', 'quality-gates', 'run-quality-gates.mjs')}\n` +
147
+ ` • Python script: ${path.join(projectRoot, 'scripts', 'simple_gates.py')}\n\n` +
148
+ 'Available options:\n' +
149
+ suggestions.map((s, i) => ` ${i + 1}. ${s}`).join('\n')
150
+ );
151
+ }
152
+
153
+ // Build command arguments
154
+ const args = ['node', qualityGatesRunner];
155
+
156
+ // Map CLI options to runner options
157
+ if (options.ci) {
158
+ args.push('--ci');
159
+ }
160
+
161
+ if (options.json) {
162
+ args.push('--json');
163
+ }
164
+
165
+ if (options.gates && options.gates.trim()) {
166
+ args.push('--gates', options.gates.trim());
167
+ }
168
+
169
+ if (options.fix) {
170
+ args.push('--fix');
171
+ }
172
+
173
+ // Add CAWS-specific environment variables for integration
174
+ const env = {
175
+ ...process.env,
176
+ CAWS_CLI_INTEGRATION: 'true',
177
+ CAWS_CLI_VERSION: require(path.join(cliPackageDir, 'package.json')).version,
178
+ };
179
+
180
+ // Set GitHub Actions summary if available
181
+ if (process.env.GITHUB_STEP_SUMMARY) {
182
+ env.GITHUB_STEP_SUMMARY = process.env.GITHUB_STEP_SUMMARY;
183
+ }
184
+
185
+ Output.progress('Executing quality gates runner...');
186
+ Output.info(`Command: ${args.join(' ')}`);
187
+
188
+ // Execute the quality gates runner with timeout
189
+ const child = spawn(args[0], args.slice(1), {
190
+ stdio: 'inherit',
191
+ cwd: packagesDir,
192
+ env: env,
462
193
  });
463
194
 
464
- child.on('error', (error) => {
465
- console.error(chalk.red('❌ Failed to execute quality gates runner:'), error.message);
466
- reject(error);
467
- });
468
- });
469
- } catch (error) {
470
- console.error(chalk.red('❌ CAWS Quality Gates command failed:'), error.message);
471
- console.error(chalk.gray('Stack trace:'), error.stack);
195
+ // Wait for completion with timeout (30 minutes default for CI)
196
+ const timeoutMs = options.timeout || (options.ci ? 30 * 60 * 1000 : 10 * 60 * 1000);
472
197
 
473
- // Provide helpful troubleshooting
474
- console.log(chalk.yellow('\n🔧 Troubleshooting:'));
475
- console.log(chalk.gray(" • Ensure you're running from the project root"));
476
- console.log(
477
- chalk.gray(' • Check that quality gates are installed: ls packages/quality-gates/')
478
- );
479
- console.log(chalk.gray(' • Verify Node.js version: node --version'));
480
- console.log(
481
- chalk.gray(' • Try direct execution: node packages/quality-gates/run-quality-gates.mjs')
482
- );
198
+ const completionPromise = new Promise((resolve, reject) => {
199
+ child.on('close', (code) => {
200
+ if (code === 0) {
201
+ resolve();
202
+ } else {
203
+ reject(new Error(`Quality gates failed with exit code: ${code}`));
204
+ }
205
+ });
483
206
 
484
- throw error;
485
- }
207
+ child.on('error', (error) => {
208
+ reject(new Error(`Failed to execute quality gates runner: ${error.message}`));
209
+ });
210
+ });
211
+
212
+ await withTimeout(completionPromise, timeoutMs, 'Quality gates execution timed out');
213
+ Output.success('Quality gates completed successfully');
214
+ },
215
+ {
216
+ commandName: 'quality-gates',
217
+ context: { options },
218
+ exitOnError: !options.ci, // Don't exit in CI mode if we want to handle errors
219
+ }
220
+ );
486
221
  }
487
222
 
488
223
  module.exports = {