@paths.design/caws-cli 7.0.3 ā 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/budget-derivation.d.ts.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/provenance.d.ts +1 -1
- package/dist/commands/provenance.d.ts.map +1 -1
- package/dist/commands/quality-gates.d.ts +0 -46
- package/dist/commands/quality-gates.d.ts.map +1 -1
- package/dist/commands/quality-gates.js +226 -11
- package/dist/commands/specs.d.ts.map +1 -1
- package/dist/commands/specs.js +108 -13
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/templates.d.ts.map +1 -1
- package/dist/commands/tool.d.ts +1 -1
- package/dist/commands/tool.d.ts.map +1 -1
- package/dist/commands/tool.js +0 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/waivers.d.ts +1 -1
- package/dist/commands/waivers.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/generators/working-spec.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/policy/PolicyManager.d.ts.map +1 -1
- package/dist/scaffold/cursor-hooks.d.ts.map +1 -1
- package/dist/scaffold/git-hooks.d.ts +18 -0
- package/dist/scaffold/git-hooks.d.ts.map +1 -1
- package/dist/scaffold/git-hooks.js +159 -58
- package/dist/scaffold/index.d.ts +1 -6
- package/dist/scaffold/index.d.ts.map +1 -1
- package/dist/scaffold/index.js +1 -1
- package/dist/templates/.caws/tools/README.md +1 -0
- package/dist/tool-loader.d.ts.map +1 -1
- package/dist/tool-validator.d.ts.map +1 -1
- package/dist/utils/async-utils.d.ts +73 -0
- package/dist/utils/async-utils.d.ts.map +1 -0
- package/dist/utils/command-wrapper.d.ts +66 -0
- package/dist/utils/command-wrapper.d.ts.map +1 -0
- package/dist/utils/detection.d.ts +7 -0
- package/dist/utils/detection.d.ts.map +1 -1
- package/dist/utils/git-lock.d.ts +13 -0
- package/dist/utils/git-lock.d.ts.map +1 -0
- package/dist/utils/git-lock.js +1 -0
- package/dist/utils/gitignore-updater.d.ts +39 -0
- package/dist/utils/gitignore-updater.d.ts.map +1 -0
- package/dist/utils/project-analysis.d.ts +20 -0
- package/dist/utils/project-analysis.d.ts.map +1 -1
- package/dist/utils/project-analysis.js +176 -16
- package/dist/utils/promise-utils.d.ts +30 -0
- package/dist/utils/promise-utils.d.ts.map +1 -0
- package/dist/utils/quality-gates.d.ts.map +1 -1
- package/dist/utils/quality-gates.js +7 -6
- package/dist/utils/spec-resolver.d.ts +1 -9
- package/dist/utils/spec-resolver.d.ts.map +1 -1
- package/dist/utils/spec-resolver.js +4 -0
- package/dist/utils/yaml-validation.d.ts +32 -0
- package/dist/utils/yaml-validation.d.ts.map +1 -0
- package/dist/utils/yaml-validation.js +1 -0
- package/dist/validation/spec-validation.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/.caws/tools/README.md +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"budget-derivation.d.ts","sourceRoot":"","sources":["../src/budget-derivation.js"],"names":[],"mappings":"AA8JA;;;;;;;;GAQG;AACH,sDALW,MAAM,YAEd;IAAyB,QAAQ,EAAzB,OAAO;CACf,
|
|
1
|
+
{"version":3,"file":"budget-derivation.d.ts","sourceRoot":"","sources":["../src/budget-derivation.js"],"names":[],"mappings":"AA8JA;;;;;;;;GAQG;AACH,sDALW,MAAM,YAEd;IAAyB,QAAQ,EAAzB,OAAO;CACf,OAgGF;AA4FD;;;;;;GAMG;AACH,qCAJW,MAAM,eACN,MAAM,GACJ,MAAO,IAAI,CAuCvB;AAED;;;;;;GAMG;AACH,0DAFa,OAAO,CAgDnB;AAED;;;;;GAKG;AACH,kFA8BC;AAqCD;;;;;;GAMG;AACH,6EAFa,MAAM,CA8ClB;AAtFD;;;;GAIG;AACH,uEAiBC;AAED;;;;;GAKG;AACH,4EAHW,MAAM,GACJ,OAAO,CAKnB;AAjgBD;;;;GAIG;AACH,kDA+FC;AAED;;;GAGG;AACH,wCAsCC;AA2GD;;;;GAIG;AACH,2DAmFC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose.js"],"names":[],"mappings":"AA0bA;;;GAGG;AACH,8DAoDC;AAvND;;;GAGG;AACH,gCAFa,OAAO,KAAQ,CAkD3B;AAED;;;GAGG;AACH,wCAFW,KAAQ,QA4ClB;AAED;;;;GAIG;AACH,wCAHW,KAAQ,GACN,OAAO,KAAQ,CAoD3B;AAzaD;;;GAGG;AACH,oCAFa,OAAO,KAAQ,CA2C3B;AAED;;;GAGG;AACH,iCAFa,OAAO,KAAQ,CAiB3B;AAED;;;GAGG;AACH,iCAFa,OAAO,KAAQ,CA0C3B;AAED;;;GAGG;AACH,yCAFa,OAAO,KAAQ,CAgE3B;AAED;;;GAGG;AACH,kCAFa,OAAO,KAAQ,CAoC3B;AAED;;;GAGG;AACH,kCAFa,OAAO,KAAQ,CA0C3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.js"],"names":[],"mappings":"AAqBA;;GAEG;AACH,2EAwiBC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @param {string} subcommand - The subcommand to execute
|
|
4
4
|
* @param {Object} options - Command options
|
|
5
5
|
*/
|
|
6
|
-
export function provenanceCommand(subcommand: string, options: any): Promise<
|
|
6
|
+
export function provenanceCommand(subcommand: string, options: any): Promise<any>;
|
|
7
7
|
/**
|
|
8
8
|
* Update provenance with new commit information
|
|
9
9
|
* @param {Object} options - Command options
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/commands/provenance.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/commands/provenance.js"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,8CAHW,MAAM,8BA+BhB;AAED;;;GAGG;AACH,8DA4EC;AAED;;;GAGG;AACH,4DAyEC;AAED;;;GAGG;AACH,8DA8CC;AA6iBD;;;GAGG;AACH,4DAmEC;AAzcD;;;GAGG;AACH,0DAwEC"}
|
|
@@ -1,49 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Quality Gates Configuration
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Update provenance with quality gates results
|
|
6
|
-
* @param {Object} results - Quality gates results
|
|
7
|
-
* @param {boolean} crisisMode - Whether in crisis mode
|
|
8
|
-
* @param {string[]} stagedFiles - Array of staged files
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Detect agent type for provenance tracking
|
|
12
|
-
* @returns {string} Agent type identifier
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Check if a waiver applies to the given gate
|
|
16
|
-
* @param {string} gate - Gate name to check
|
|
17
|
-
* @returns {Object} Waiver check result
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Detect if project is in crisis response mode
|
|
21
|
-
* @returns {boolean} True if in crisis mode
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* Get staged files from git
|
|
25
|
-
* @returns {string[]} Array of staged file paths
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* Check for god objects in staged Rust files with waiver and crisis mode support
|
|
29
|
-
* @param {string[]} stagedFiles - Array of staged file paths
|
|
30
|
-
* @param {boolean} crisisMode - Whether in crisis response mode
|
|
31
|
-
* @returns {Object} God object analysis results
|
|
32
|
-
*/
|
|
33
|
-
/**
|
|
34
|
-
* Check for hidden TODOs in staged files with waiver and crisis mode support
|
|
35
|
-
* @param {string[]} stagedFiles - Array of staged file paths
|
|
36
|
-
* @param {boolean} crisisMode - Whether in crisis response mode
|
|
37
|
-
* @returns {Object} TODO analysis results
|
|
38
|
-
*/
|
|
39
|
-
/**
|
|
40
|
-
* Check for human override in working spec
|
|
41
|
-
* @returns {Object} Human override check result
|
|
42
|
-
*/
|
|
43
|
-
/**
|
|
44
|
-
* Get CAWS tier from working spec
|
|
45
|
-
* @returns {number|null} CAWS tier (1, 2, or 3) or null if not found
|
|
46
|
-
*/
|
|
47
1
|
/**
|
|
48
2
|
* Run comprehensive quality gates on staged files
|
|
49
3
|
* @param {Object} options - Command options
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quality-gates.d.ts","sourceRoot":"","sources":["../../src/commands/quality-gates.js"],"names":[],"mappings":"AAqBA
|
|
1
|
+
{"version":3,"file":"quality-gates.d.ts","sourceRoot":"","sources":["../../src/commands/quality-gates.js"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,iEA4ZC"}
|
|
@@ -44,14 +44,66 @@ async function qualityGatesCommand(options = {}) {
|
|
|
44
44
|
const packagesDir = path.dirname(cliPackageDir);
|
|
45
45
|
const monorepoRunner = path.join(packagesDir, 'quality-gates', 'run-quality-gates.mjs');
|
|
46
46
|
|
|
47
|
-
// Option 2: Check
|
|
47
|
+
// Option 2: Check globally installed CLI for bundled quality gates
|
|
48
|
+
let globalCliPath = null;
|
|
49
|
+
try {
|
|
50
|
+
const { execSync } = require('child_process');
|
|
51
|
+
const whichCaws = execSync('which caws', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
52
|
+
if (whichCaws) {
|
|
53
|
+
// Resolve symlink to actual path
|
|
54
|
+
const realPath = fs.realpathSync(whichCaws);
|
|
55
|
+
const globalCliDir = path.dirname(realPath);
|
|
56
|
+
// Check for bundled quality gates in global CLI installation
|
|
57
|
+
const possibleBundledPaths = [
|
|
58
|
+
path.join(
|
|
59
|
+
globalCliDir,
|
|
60
|
+
'..',
|
|
61
|
+
'lib',
|
|
62
|
+
'node_modules',
|
|
63
|
+
'@paths.design',
|
|
64
|
+
'caws-cli',
|
|
65
|
+
'node_modules',
|
|
66
|
+
'@paths.design',
|
|
67
|
+
'quality-gates',
|
|
68
|
+
'run-quality-gates.mjs'
|
|
69
|
+
),
|
|
70
|
+
path.join(
|
|
71
|
+
globalCliDir,
|
|
72
|
+
'..',
|
|
73
|
+
'lib',
|
|
74
|
+
'node_modules',
|
|
75
|
+
'@paths.design',
|
|
76
|
+
'quality-gates',
|
|
77
|
+
'run-quality-gates.mjs'
|
|
78
|
+
),
|
|
79
|
+
path.join(
|
|
80
|
+
globalCliDir,
|
|
81
|
+
'..',
|
|
82
|
+
'node_modules',
|
|
83
|
+
'@paths.design',
|
|
84
|
+
'quality-gates',
|
|
85
|
+
'run-quality-gates.mjs'
|
|
86
|
+
),
|
|
87
|
+
];
|
|
88
|
+
for (const bundledPath of possibleBundledPaths) {
|
|
89
|
+
if (fs.existsSync(bundledPath)) {
|
|
90
|
+
globalCliPath = bundledPath;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
// Ignore errors finding global CLI
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Option 3: Check VS Code extension bundled (if running from extension context)
|
|
48
100
|
const vscodeExtensionPath =
|
|
49
101
|
process.env.VSCODE_EXTENSION_PATH || process.env.VSCODE_EXTENSION_DIR;
|
|
50
102
|
const bundledRunner = vscodeExtensionPath
|
|
51
103
|
? path.join(vscodeExtensionPath, 'bundled', 'quality-gates', 'run-quality-gates.mjs')
|
|
52
104
|
: null;
|
|
53
105
|
|
|
54
|
-
// Option
|
|
106
|
+
// Option 4: Check node_modules for quality-gates package (prioritize published package)
|
|
55
107
|
const nodeModulesPaths = [
|
|
56
108
|
// Published npm package (priority)
|
|
57
109
|
path.join(
|
|
@@ -69,6 +121,8 @@ async function qualityGatesCommand(options = {}) {
|
|
|
69
121
|
// Try all possible paths in order
|
|
70
122
|
if (fs.existsSync(monorepoRunner)) {
|
|
71
123
|
qualityGatesRunner = monorepoRunner;
|
|
124
|
+
} else if (globalCliPath) {
|
|
125
|
+
qualityGatesRunner = globalCliPath;
|
|
72
126
|
} else if (bundledRunner && fs.existsSync(bundledRunner)) {
|
|
73
127
|
qualityGatesRunner = bundledRunner;
|
|
74
128
|
} else {
|
|
@@ -132,14 +186,104 @@ async function qualityGatesCommand(options = {}) {
|
|
|
132
186
|
}
|
|
133
187
|
}
|
|
134
188
|
|
|
135
|
-
//
|
|
189
|
+
// Option 5: Try npx (no installation required) - works if Node.js is available
|
|
136
190
|
if (!qualityGatesRunner) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
'
|
|
141
|
-
|
|
142
|
-
|
|
191
|
+
try {
|
|
192
|
+
const { execSync } = require('child_process');
|
|
193
|
+
// Check if npx is available
|
|
194
|
+
execSync('command -v npx', { encoding: 'utf8', stdio: 'ignore' });
|
|
195
|
+
|
|
196
|
+
Output.info('Using npx to run quality gates (no installation required)...');
|
|
197
|
+
|
|
198
|
+
// Build npx command - the package exposes 'caws-quality-gates' bin command
|
|
199
|
+
// Use npx to download and run without installing
|
|
200
|
+
const npxArgs = ['npx', '--yes', '@paths.design/quality-gates'];
|
|
201
|
+
|
|
202
|
+
// Map CLI options to runner options
|
|
203
|
+
if (options.ci) {
|
|
204
|
+
npxArgs.push('--ci');
|
|
205
|
+
}
|
|
206
|
+
if (options.json) {
|
|
207
|
+
npxArgs.push('--json');
|
|
208
|
+
}
|
|
209
|
+
if (options.gates && options.gates.trim()) {
|
|
210
|
+
npxArgs.push('--gates', options.gates.trim());
|
|
211
|
+
}
|
|
212
|
+
if (options.fix) {
|
|
213
|
+
npxArgs.push('--fix');
|
|
214
|
+
}
|
|
215
|
+
// Handle context options: --all-files takes precedence, then --context
|
|
216
|
+
if (options.allFiles) {
|
|
217
|
+
npxArgs.push('--context=ci');
|
|
218
|
+
} else if (options.context && options.context !== 'commit') {
|
|
219
|
+
npxArgs.push(`--context=${options.context}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
Output.progress('Executing quality gates via npx...');
|
|
223
|
+
Output.info(`Command: ${npxArgs.join(' ')}`);
|
|
224
|
+
|
|
225
|
+
// Execute via npx
|
|
226
|
+
const { execSync: execSyncNpx } = require('child_process');
|
|
227
|
+
execSyncNpx(npxArgs.join(' '), {
|
|
228
|
+
stdio: 'inherit',
|
|
229
|
+
cwd: projectRoot,
|
|
230
|
+
env: {
|
|
231
|
+
...process.env,
|
|
232
|
+
CAWS_CLI_INTEGRATION: 'true',
|
|
233
|
+
CAWS_CLI_VERSION: require(path.join(cliPackageDir, 'package.json')).version,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
Output.success('Quality gates completed successfully');
|
|
238
|
+
return;
|
|
239
|
+
} catch (npxError) {
|
|
240
|
+
// npx not available or failed - continue to error message
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// If still no runner found, provide helpful error with language-agnostic suggestions
|
|
245
|
+
if (!qualityGatesRunner) {
|
|
246
|
+
// Check if Node.js/npx is available (language-agnostic check)
|
|
247
|
+
let hasNodeJs = false;
|
|
248
|
+
try {
|
|
249
|
+
const { execSync } = require('child_process');
|
|
250
|
+
execSync('command -v node', { encoding: 'utf8', stdio: 'ignore' });
|
|
251
|
+
execSync('command -v npx', { encoding: 'utf8', stdio: 'ignore' });
|
|
252
|
+
hasNodeJs = true;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
// Node.js/npx not available
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const suggestions = [];
|
|
258
|
+
|
|
259
|
+
if (hasNodeJs) {
|
|
260
|
+
// Node.js available - suggest npx (works for any language, no installation)
|
|
261
|
+
suggestions.push(
|
|
262
|
+
'Use npx (no installation required): npx --yes @paths.design/quality-gates'
|
|
263
|
+
);
|
|
264
|
+
suggestions.push('Install globally: npm install -g @paths.design/quality-gates');
|
|
265
|
+
suggestions.push('Install locally: npm install --save-dev @paths.design/quality-gates');
|
|
266
|
+
} else {
|
|
267
|
+
// Node.js not available - suggest installation or alternatives
|
|
268
|
+
suggestions.push('Install Node.js to use quality gates: https://nodejs.org/');
|
|
269
|
+
suggestions.push(
|
|
270
|
+
'Then use: npx --yes @paths.design/quality-gates (no installation required)'
|
|
271
|
+
);
|
|
272
|
+
suggestions.push('Or install globally: npm install -g @paths.design/quality-gates');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Language-agnostic fallback options (if they exist)
|
|
276
|
+
const pythonScript = path.join(projectRoot, 'scripts', 'simple_gates.py');
|
|
277
|
+
const makefile = path.join(projectRoot, 'Makefile');
|
|
278
|
+
|
|
279
|
+
if (fs.existsSync(pythonScript)) {
|
|
280
|
+
suggestions.push(`Use project script: python3 ${pythonScript} all --tier 2`);
|
|
281
|
+
}
|
|
282
|
+
if (fs.existsSync(makefile)) {
|
|
283
|
+
suggestions.push('Use Makefile target: make caws-gates');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
suggestions.push('Run from CAWS monorepo root (if developing CAWS itself)');
|
|
143
287
|
|
|
144
288
|
throw new Error(
|
|
145
289
|
'Quality gates runner not found.\n\n' +
|
|
@@ -172,6 +316,13 @@ async function qualityGatesCommand(options = {}) {
|
|
|
172
316
|
args.push('--fix');
|
|
173
317
|
}
|
|
174
318
|
|
|
319
|
+
// Handle context options: --all-files takes precedence, then --context
|
|
320
|
+
if (options.allFiles) {
|
|
321
|
+
args.push('--context=ci');
|
|
322
|
+
} else if (options.context && options.context !== 'commit') {
|
|
323
|
+
args.push(`--context=${options.context}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
175
326
|
// Add CAWS-specific environment variables for integration
|
|
176
327
|
const env = {
|
|
177
328
|
...process.env,
|
|
@@ -198,7 +349,47 @@ async function qualityGatesCommand(options = {}) {
|
|
|
198
349
|
const timeoutMs = options.timeout || (options.ci ? 30 * 60 * 1000 : 10 * 60 * 1000);
|
|
199
350
|
|
|
200
351
|
const completionPromise = new Promise((resolve, reject) => {
|
|
352
|
+
let resolved = false;
|
|
353
|
+
|
|
354
|
+
// Handle process termination signals
|
|
355
|
+
const signalHandlers = {
|
|
356
|
+
SIGINT: () => {
|
|
357
|
+
if (!resolved && !child.killed && child.pid) {
|
|
358
|
+
child.kill('SIGINT');
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
SIGTERM: () => {
|
|
362
|
+
if (!resolved && !child.killed && child.pid) {
|
|
363
|
+
child.kill('SIGTERM');
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
process.on('SIGINT', signalHandlers.SIGINT);
|
|
369
|
+
process.on('SIGTERM', signalHandlers.SIGTERM);
|
|
370
|
+
|
|
371
|
+
const cleanup = () => {
|
|
372
|
+
if (resolved) return;
|
|
373
|
+
resolved = true;
|
|
374
|
+
|
|
375
|
+
// Remove signal handlers
|
|
376
|
+
try {
|
|
377
|
+
process.removeListener('SIGINT', signalHandlers.SIGINT);
|
|
378
|
+
process.removeListener('SIGTERM', signalHandlers.SIGTERM);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
// Ignore errors removing listeners
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Remove event listeners to prevent memory leaks
|
|
384
|
+
try {
|
|
385
|
+
child.removeAllListeners();
|
|
386
|
+
} catch (e) {
|
|
387
|
+
// Ignore errors removing listeners
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
201
391
|
child.on('close', (code) => {
|
|
392
|
+
cleanup();
|
|
202
393
|
if (code === 0) {
|
|
203
394
|
resolve();
|
|
204
395
|
} else {
|
|
@@ -207,12 +398,36 @@ async function qualityGatesCommand(options = {}) {
|
|
|
207
398
|
});
|
|
208
399
|
|
|
209
400
|
child.on('error', (error) => {
|
|
401
|
+
cleanup();
|
|
210
402
|
reject(new Error(`Failed to execute quality gates runner: ${error.message}`));
|
|
211
403
|
});
|
|
212
404
|
});
|
|
213
405
|
|
|
214
|
-
|
|
215
|
-
|
|
406
|
+
try {
|
|
407
|
+
await withTimeout(completionPromise, timeoutMs, 'Quality gates execution timed out');
|
|
408
|
+
Output.success('Quality gates completed successfully');
|
|
409
|
+
} catch (error) {
|
|
410
|
+
// Ensure child process is killed on timeout or error
|
|
411
|
+
try {
|
|
412
|
+
if (!child.killed && child.pid) {
|
|
413
|
+
child.kill('SIGTERM');
|
|
414
|
+
// Give it a moment to exit gracefully, then force kill
|
|
415
|
+
// eslint-disable-next-line no-undef
|
|
416
|
+
setTimeout(() => {
|
|
417
|
+
if (!child.killed && child.pid) {
|
|
418
|
+
try {
|
|
419
|
+
child.kill('SIGKILL');
|
|
420
|
+
} catch (e) {
|
|
421
|
+
// Ignore errors killing process
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}, 1000);
|
|
425
|
+
}
|
|
426
|
+
} catch (killError) {
|
|
427
|
+
// Ignore errors killing process
|
|
428
|
+
}
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
216
431
|
},
|
|
217
432
|
{
|
|
218
433
|
commandName: 'quality-gates',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../src/commands/specs.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../src/commands/specs.js"],"names":[],"mappings":"AAipBA;;;;GAIG;AACH,qCAHW,MAAM,+BA+LhB;AA3zBD;;;GAGG;AACH,qCAFa,OAAO,KAAQ,CAqB3B;AAED;;;;GAIG;AACH,kDAFa,OAAO,CAAC,IAAI,CAAC,CAMzB;AAED;;;GAGG;AACH,iCAFa,OAAO,OAAO,CAmC1B;AAED;;;;;GAKG;AACH,+BAJW,MAAM,kBAEJ,OAAO,KAAQ,CAkO3B;AAED;;;;GAIG;AACH,6BAHW,MAAM,GACJ,OAAO,CAAC,MAAO,IAAI,CAAC,CAiBhC;AAED;;;;;GAKG;AACH,+BAJW,MAAM,kBAEJ,OAAO,CAAC,OAAO,CAAC,CA6B5B;AAED;;;;GAIG;AACH,+BAHW,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAmB5B;AAED;;;GAGG;AACH,sDA2CC;AAED;;;GAGG;AACH,oDAoCC;AAoHD;;;GAGG;AACH,yCAFa,OAAO,CAAC,MAAM,CAAC,CAqC3B;AA9nBD;;GAEG;AACH,wBAAkB,aAAa,CAAC;AAChC,6BAAuB,2BAA2B,CAAC"}
|
package/dist/commands/specs.js
CHANGED
|
@@ -154,9 +154,13 @@ async function createSpec(id, options = {}) {
|
|
|
154
154
|
console.log(chalk.blue('ā¹ļø Spec creation canceled.'));
|
|
155
155
|
return null;
|
|
156
156
|
} else if (answer === 'rename') {
|
|
157
|
-
// Generate new name with
|
|
158
|
-
|
|
159
|
-
const
|
|
157
|
+
// Generate new name with valid PREFIX-NUMBER format
|
|
158
|
+
// Extract prefix from existing ID or use default
|
|
159
|
+
const prefixMatch = id.match(/^([A-Z]+)-\d+$/);
|
|
160
|
+
const prefix = prefixMatch ? prefixMatch[1] : 'FEAT';
|
|
161
|
+
// Generate sequential number based on timestamp
|
|
162
|
+
const number = Date.now().toString().slice(-6); // Last 6 digits of timestamp
|
|
163
|
+
const newId = `${prefix}-${number}`;
|
|
160
164
|
console.log(chalk.blue(`š Creating spec with new name: ${newId}`));
|
|
161
165
|
return await createSpec(newId, { ...options, interactive: false });
|
|
162
166
|
} else if (answer === 'merge') {
|
|
@@ -183,9 +187,10 @@ async function createSpec(id, options = {}) {
|
|
|
183
187
|
// Ensure specs directory exists
|
|
184
188
|
await fs.ensureDir(SPECS_DIR);
|
|
185
189
|
|
|
186
|
-
// Generate spec content
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
// Generate spec content with all required fields
|
|
191
|
+
// Merge template carefully to preserve required fields and structure
|
|
192
|
+
const defaultSpec = {
|
|
193
|
+
id, // Always use the provided id parameter
|
|
189
194
|
type,
|
|
190
195
|
title,
|
|
191
196
|
status: 'draft',
|
|
@@ -193,8 +198,64 @@ async function createSpec(id, options = {}) {
|
|
|
193
198
|
mode,
|
|
194
199
|
created_at: new Date().toISOString(),
|
|
195
200
|
updated_at: new Date().toISOString(),
|
|
196
|
-
|
|
201
|
+
// Required fields for validation
|
|
202
|
+
blast_radius: {
|
|
203
|
+
modules: [],
|
|
204
|
+
data_migration: false,
|
|
205
|
+
},
|
|
206
|
+
operational_rollback_slo: '5m',
|
|
207
|
+
scope: {
|
|
208
|
+
in: ['src/', 'tests/'],
|
|
209
|
+
out: ['node_modules/', 'dist/', 'build/'],
|
|
210
|
+
},
|
|
211
|
+
invariants: ['System maintains data consistency'],
|
|
212
|
+
acceptance: [], // Note: validation expects 'acceptance', not 'acceptance_criteria'
|
|
213
|
+
acceptance_criteria: [], // Keep for backward compatibility
|
|
214
|
+
non_functional: {
|
|
215
|
+
a11y: [],
|
|
216
|
+
perf: {},
|
|
217
|
+
security: [],
|
|
218
|
+
},
|
|
219
|
+
contracts: [],
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Merge template, but preserve required structure
|
|
223
|
+
// Map template.criteria to acceptance if present
|
|
224
|
+
const templateAcceptance = template?.criteria || template?.acceptance;
|
|
225
|
+
|
|
226
|
+
const specContent = {
|
|
227
|
+
...defaultSpec,
|
|
197
228
|
...(template || {}),
|
|
229
|
+
// Always preserve these critical fields
|
|
230
|
+
id, // Never allow template to override id
|
|
231
|
+
// Map criteria to acceptance if template uses criteria
|
|
232
|
+
acceptance: templateAcceptance || defaultSpec.acceptance,
|
|
233
|
+
acceptance_criteria: templateAcceptance || defaultSpec.acceptance_criteria,
|
|
234
|
+
// Deep merge scope if template provides it
|
|
235
|
+
scope: template?.scope
|
|
236
|
+
? {
|
|
237
|
+
in: template.scope.in || defaultSpec.scope.in,
|
|
238
|
+
out: template.scope.out || defaultSpec.scope.out,
|
|
239
|
+
}
|
|
240
|
+
: defaultSpec.scope,
|
|
241
|
+
// Deep merge blast_radius if template provides it
|
|
242
|
+
blast_radius: template?.blast_radius
|
|
243
|
+
? {
|
|
244
|
+
modules: template.blast_radius.modules || defaultSpec.blast_radius.modules,
|
|
245
|
+
data_migration:
|
|
246
|
+
template.blast_radius.data_migration !== undefined
|
|
247
|
+
? template.blast_radius.data_migration
|
|
248
|
+
: defaultSpec.blast_radius.data_migration,
|
|
249
|
+
}
|
|
250
|
+
: defaultSpec.blast_radius,
|
|
251
|
+
// Deep merge non_functional if template provides it
|
|
252
|
+
non_functional: template?.non_functional
|
|
253
|
+
? {
|
|
254
|
+
a11y: template.non_functional.a11y || defaultSpec.non_functional.a11y,
|
|
255
|
+
perf: template.non_functional.perf || defaultSpec.non_functional.perf,
|
|
256
|
+
security: template.non_functional.security || defaultSpec.non_functional.security,
|
|
257
|
+
}
|
|
258
|
+
: defaultSpec.non_functional,
|
|
198
259
|
};
|
|
199
260
|
|
|
200
261
|
// Create file path
|
|
@@ -442,9 +503,10 @@ function displaySpecDetails(spec) {
|
|
|
442
503
|
/**
|
|
443
504
|
* Migrate from legacy working-spec.yaml to feature-specific specs
|
|
444
505
|
* @param {Object} options - Migration options
|
|
506
|
+
* @param {Function} [createSpecFn] - Function to create specs (for testing)
|
|
445
507
|
* @returns {Promise<Object>} Migration result
|
|
446
508
|
*/
|
|
447
|
-
async function migrateFromLegacy(options = {}) {
|
|
509
|
+
async function migrateFromLegacy(options = {}, createSpecFn = createSpec) {
|
|
448
510
|
const fs = require('fs-extra');
|
|
449
511
|
const path = require('path');
|
|
450
512
|
const yaml = require('js-yaml');
|
|
@@ -461,6 +523,14 @@ async function migrateFromLegacy(options = {}) {
|
|
|
461
523
|
const legacyContent = await fs.readFile(legacyPath, 'utf8');
|
|
462
524
|
const legacySpec = yaml.load(legacyContent);
|
|
463
525
|
|
|
526
|
+
if (!legacySpec) {
|
|
527
|
+
throw new Error('Legacy working-spec.yaml is empty or invalid');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (!legacySpec.acceptance || !Array.isArray(legacySpec.acceptance)) {
|
|
531
|
+
throw new Error('Legacy working-spec.yaml must have an acceptance array');
|
|
532
|
+
}
|
|
533
|
+
|
|
464
534
|
// Suggest feature breakdown based on acceptance criteria
|
|
465
535
|
const features = suggestFeatureBreakdown(legacySpec);
|
|
466
536
|
|
|
@@ -480,15 +550,32 @@ async function migrateFromLegacy(options = {}) {
|
|
|
480
550
|
}
|
|
481
551
|
|
|
482
552
|
if (options.features && options.features.length > 0) {
|
|
553
|
+
// Filter by original feature IDs (before transformation)
|
|
483
554
|
selectedFeatures = features.filter((f) => options.features.includes(f.id));
|
|
484
|
-
|
|
555
|
+
if (selectedFeatures.length === 0) {
|
|
556
|
+
const errorMsg = `No features found matching: ${options.features.join(', ')}. Available features: ${features.map((f) => f.id).join(', ')}`;
|
|
557
|
+
console.log(chalk.yellow(`ā ļø ${errorMsg}`));
|
|
558
|
+
throw new Error(errorMsg);
|
|
559
|
+
} else {
|
|
560
|
+
console.log(chalk.blue(`\nš Migrating selected features: ${options.features.join(', ')}`));
|
|
561
|
+
}
|
|
485
562
|
}
|
|
486
563
|
|
|
487
564
|
// Create each feature spec
|
|
488
565
|
const createdSpecs = [];
|
|
566
|
+
let featureCounter = 1;
|
|
489
567
|
for (const feature of selectedFeatures) {
|
|
490
568
|
try {
|
|
491
|
-
|
|
569
|
+
// Transform feature ID to proper format (PREFIX-NUMBER) if needed
|
|
570
|
+
let specId = feature.id;
|
|
571
|
+
if (!/^[A-Z]+-\d+$/.test(specId)) {
|
|
572
|
+
// Convert 'auth' -> 'FEAT-001', 'payment' -> 'FEAT-002', etc.
|
|
573
|
+
const prefix = specId.toUpperCase().replace(/[^A-Z0-9]/g, '');
|
|
574
|
+
specId = `${prefix || 'FEAT'}-${String(featureCounter).padStart(3, '0')}`;
|
|
575
|
+
featureCounter++;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
await createSpecFn(specId, {
|
|
492
579
|
type: 'feature',
|
|
493
580
|
title: feature.title,
|
|
494
581
|
risk_tier: 'T3', // Default tier
|
|
@@ -496,10 +583,14 @@ async function migrateFromLegacy(options = {}) {
|
|
|
496
583
|
template: feature,
|
|
497
584
|
});
|
|
498
585
|
|
|
499
|
-
createdSpecs.push(
|
|
500
|
-
console.log(chalk.green(` ā
Created spec: ${
|
|
586
|
+
createdSpecs.push(specId);
|
|
587
|
+
console.log(chalk.green(` ā
Created spec: ${specId}`));
|
|
501
588
|
} catch (error) {
|
|
589
|
+
// Log full error details for debugging
|
|
502
590
|
console.log(chalk.red(` ā Failed to create spec ${feature.id}: ${error.message}`));
|
|
591
|
+
if (process.env.DEBUG_MIGRATION) {
|
|
592
|
+
console.log(chalk.gray(` Error details: ${error.stack}`));
|
|
593
|
+
}
|
|
503
594
|
}
|
|
504
595
|
}
|
|
505
596
|
|
|
@@ -628,7 +719,11 @@ async function specsCommand(action, options = {}) {
|
|
|
628
719
|
}
|
|
629
720
|
|
|
630
721
|
case 'migrate': {
|
|
631
|
-
|
|
722
|
+
// Allow tests to inject createSpec function
|
|
723
|
+
const createSpecFn = options._createSpecFn || createSpec;
|
|
724
|
+
const migrationOptions = { ...options };
|
|
725
|
+
delete migrationOptions._createSpecFn; // Remove test-only option
|
|
726
|
+
const result = await migrateFromLegacy(migrationOptions, createSpecFn);
|
|
632
727
|
|
|
633
728
|
return outputResult({
|
|
634
729
|
command: 'specs migrate',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.js"],"names":[],"mappings":"AA8uBA;;;GAGG;AACH,2DA+IC;AAp3BD;;;;GAIG;AACH,2CAHW,MAAM,GACJ,OAAO,CAAC,MAAO,IAAI,CAAC,CAahC;AAgBD;;;GAGG;AACH,iCAFa,OAAO,KAAQ,CAgC3B;AAED;;;GAGG;AACH,uCAFa,OAAO,KAAQ,CA+B3B;AAED;;;GAGG;AACH,oCAFa,OAAO,KAAQ,CA0D3B;AAED;;;GAGG;AACH,qCAFa,OAAO,KAAQ,CAS3B;AAwBD;;;GAGG;AACH,+CAgGC;AAED;;;;;GAKG;AACH,4DAHW,MAAM,GACJ,MAAM,EAAE,CAoCpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/commands/templates.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/commands/templates.js"],"names":[],"mappings":"AA8MA;;;;GAIG;AACH,8CAHW,MAAM,gCA4BhB;AAvHD;;GAEG;AACH,sCAuCC;AAED;;;GAGG;AACH,6CAFW,MAAM,QAyChB;AA9GD;;;GAGG;AACH,6CAiBC;AAxGD;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiDE"}
|
package/dist/commands/tool.d.ts
CHANGED
|
@@ -8,6 +8,6 @@ export function initializeToolSystem(): Promise<ToolLoader | null>;
|
|
|
8
8
|
* @param {string} toolId - ID of the tool to execute
|
|
9
9
|
* @param {Object} options - Command options
|
|
10
10
|
*/
|
|
11
|
-
export function executeTool(toolId: string, options: any): Promise<
|
|
11
|
+
export function executeTool(toolId: string, options: any): Promise<any>;
|
|
12
12
|
import ToolLoader = require("../tool-loader");
|
|
13
13
|
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/commands/tool.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/commands/tool.js"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wCAFa,OAAO,CAAC,UAAU,GAAC,IAAI,CAAC,CAwCpC;AAED;;;;GAIG;AACH,oCAHW,MAAM,8BAoEhB"}
|
package/dist/commands/tool.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.js"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH,0CANW,MAAM,YAEd;IAAyB,MAAM,GAAvB,MAAM;IACY,WAAW,GAA7B,OAAO;IACW,MAAM,GAAxB,OAAO;CACjB,
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.js"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH,0CANW,MAAM,YAEd;IAAyB,MAAM,GAAvB,MAAM;IACY,WAAW,GAA7B,OAAO;IACW,MAAM,GAAxB,OAAO;CACjB,iBA6NA"}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* @param {string} subcommand - create, list, show, revoke
|
|
5
5
|
* @param {object} options - Command options
|
|
6
6
|
*/
|
|
7
|
-
export function waiversCommand(subcommand?: string, options?: object): Promise<
|
|
7
|
+
export function waiversCommand(subcommand?: string, options?: object): Promise<any>;
|
|
8
8
|
//# sourceMappingURL=waivers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waivers.d.ts","sourceRoot":"","sources":["../../src/commands/waivers.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"waivers.d.ts","sourceRoot":"","sources":["../../src/commands/waivers.js"],"names":[],"mappings":"AAoBA;;;;;GAKG;AACH,4CAHW,MAAM,YACN,MAAM,gBAyChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.js"],"names":[],"mappings":";mCAakD,OAAO;AAOzD;;;GAGG;AACH,6CAkBC;AAED;;;GAGG;AACH,uCAFa,MAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.js"],"names":[],"mappings":";mCAakD,OAAO;AAOzD;;;GAGG;AACH,6CAkBC;AAED;;;GAGG;AACH,uCAFa,MAAO,IAAI,CA+CvB;AAED;;;GAGG;AACH,6CAFa,MAAO,IAAI,CAwCvB;AAED;;;GAGG;AACH,0CAEC;AAED;;;GAGG;AACH,qDAEC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"working-spec.d.ts","sourceRoot":"","sources":["../../src/generators/working-spec.js"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,mDAFa,MAAM,
|
|
1
|
+
{"version":3,"file":"working-spec.d.ts","sourceRoot":"","sources":["../../src/generators/working-spec.js"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,mDAFa,MAAM,CAsLlB;AAED;;;;GAIG;AACH,mDAHW,MAAM,uBA8BhB"}
|