@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.
- package/README.md +15 -12
- package/dist/budget-derivation.d.ts +74 -0
- package/dist/budget-derivation.d.ts.map +1 -0
- package/dist/cicd-optimizer.d.ts +142 -0
- package/dist/cicd-optimizer.d.ts.map +1 -0
- package/dist/commands/archive.d.ts +50 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/burnup.d.ts +6 -0
- package/dist/commands/burnup.d.ts.map +1 -0
- package/dist/commands/diagnose.d.ts +52 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/evaluate.d.ts +8 -0
- package/dist/commands/evaluate.d.ts.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/iterate.d.ts +8 -0
- package/dist/commands/iterate.d.ts.map +1 -0
- package/dist/commands/mode.d.ts +24 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/plan.d.ts +49 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/provenance.d.ts +32 -0
- package/dist/commands/provenance.d.ts.map +1 -0
- package/dist/commands/provenance.js +27 -22
- package/dist/commands/quality-gates.d.ts +52 -0
- package/dist/commands/quality-gates.d.ts.map +1 -0
- package/dist/commands/quality-gates.js +190 -455
- package/dist/commands/quality-monitor.d.ts +17 -0
- package/dist/commands/quality-monitor.d.ts.map +1 -0
- package/dist/commands/specs.d.ts +71 -0
- package/dist/commands/specs.d.ts.map +1 -0
- package/dist/commands/specs.js +34 -35
- package/dist/commands/status.d.ts +44 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +10 -7
- package/dist/commands/templates.d.ts +74 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/tool.d.ts +13 -0
- package/dist/commands/tool.d.ts.map +1 -0
- package/dist/commands/tool.js +63 -63
- package/dist/commands/troubleshoot.d.ts +8 -0
- package/dist/commands/troubleshoot.d.ts.map +1 -0
- package/dist/commands/tutorial.d.ts +55 -0
- package/dist/commands/tutorial.d.ts.map +1 -0
- package/dist/commands/validate.d.ts +15 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/waivers.d.ts +8 -0
- package/dist/commands/waivers.d.ts.map +1 -0
- package/dist/commands/waivers.js +38 -39
- package/dist/commands/workflow.d.ts +85 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/config/index.d.ts +29 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/modes.d.ts +225 -0
- package/dist/config/modes.d.ts.map +1 -0
- package/dist/constants/spec-types.d.ts +41 -0
- package/dist/constants/spec-types.d.ts.map +1 -0
- package/dist/error-handler.d.ts +164 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/generators/jest-config.d.ts +32 -0
- package/dist/generators/jest-config.d.ts.map +1 -0
- package/dist/generators/working-spec.d.ts +13 -0
- package/dist/generators/working-spec.d.ts.map +1 -0
- package/dist/index-new.d.ts +5 -0
- package/dist/index-new.d.ts.map +1 -0
- package/dist/index-new.js +317 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.backup +4711 -0
- package/dist/minimal-cli.d.ts +3 -0
- package/dist/minimal-cli.d.ts.map +1 -0
- package/dist/policy/PolicyManager.d.ts +104 -0
- package/dist/policy/PolicyManager.d.ts.map +1 -0
- package/dist/policy/PolicyManager.js +60 -28
- package/dist/scaffold/cursor-hooks.d.ts +7 -0
- package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
- package/dist/scaffold/git-hooks.d.ts +20 -0
- package/dist/scaffold/git-hooks.d.ts.map +1 -0
- package/dist/scaffold/git-hooks.js +89 -27
- package/dist/scaffold/index.d.ts +20 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +25 -0
- package/dist/spec/SpecFileManager.d.ts +146 -0
- package/dist/spec/SpecFileManager.d.ts.map +1 -0
- package/dist/test-analysis.d.ts +182 -0
- package/dist/test-analysis.d.ts.map +1 -0
- package/dist/tool-interface.d.ts +236 -0
- package/dist/tool-interface.d.ts.map +1 -0
- package/dist/tool-loader.d.ts +77 -0
- package/dist/tool-loader.d.ts.map +1 -0
- package/dist/tool-validator.d.ts +72 -0
- package/dist/tool-validator.d.ts.map +1 -0
- package/dist/utils/async-utils.js +188 -0
- package/dist/utils/command-wrapper.js +200 -0
- package/dist/utils/detection.d.ts +7 -0
- package/dist/utils/detection.d.ts.map +1 -0
- package/dist/utils/finalization.d.ts +17 -0
- package/dist/utils/finalization.d.ts.map +1 -0
- package/dist/utils/project-analysis.d.ts +14 -0
- package/dist/utils/project-analysis.d.ts.map +1 -0
- package/dist/utils/promise-utils.js +72 -0
- package/dist/utils/quality-gates.d.ts +49 -0
- package/dist/utils/quality-gates.d.ts.map +1 -0
- package/dist/utils/spec-resolver.d.ts +88 -0
- package/dist/utils/spec-resolver.d.ts.map +1 -0
- package/dist/utils/typescript-detector.d.ts +63 -0
- package/dist/utils/typescript-detector.d.ts.map +1 -0
- package/dist/validation/spec-validation.d.ts +43 -0
- package/dist/validation/spec-validation.d.ts.map +1 -0
- package/dist/waivers-manager.d.ts +167 -0
- package/dist/waivers-manager.d.ts.map +1 -0
- package/package.json +1 -1
- package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
- 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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
100
|
+
pythonArgs.push('--ci');
|
|
101
|
+
}
|
|
102
|
+
if (options.json) {
|
|
103
|
+
pythonArgs.push('--json');
|
|
459
104
|
}
|
|
460
|
-
|
|
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
|
-
|
|
465
|
-
|
|
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
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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 = {
|