@paths.design/caws-cli 9.3.2 → 10.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 +71 -32
- package/dist/budget-derivation.js +221 -74
- package/dist/commands/archive.js +67 -28
- package/dist/commands/burnup.js +20 -11
- package/dist/commands/diagnose.js +34 -22
- package/dist/commands/evaluate.js +41 -15
- package/dist/commands/gates.js +149 -0
- package/dist/commands/init.js +150 -19
- package/dist/commands/iterate.js +81 -4
- package/dist/commands/parallel.js +4 -0
- package/dist/commands/plan.js +9 -19
- package/dist/commands/provenance.js +53 -17
- package/dist/commands/quality-monitor.js +64 -45
- package/dist/commands/scope.js +264 -0
- package/dist/commands/sidecar.js +74 -0
- package/dist/commands/specs.js +381 -45
- package/dist/commands/status.js +117 -9
- package/dist/commands/templates.js +0 -8
- package/dist/commands/tutorial.js +10 -9
- package/dist/commands/validate.js +70 -6
- package/dist/commands/verify-acs.js +48 -76
- package/dist/commands/waivers.js +212 -13
- package/dist/commands/worktree.js +131 -26
- package/dist/error-handler.js +2 -13
- package/dist/gates/budget-limit.js +121 -0
- package/dist/gates/feedback.js +260 -0
- package/dist/gates/format.js +179 -0
- package/dist/gates/god-object.js +117 -0
- package/dist/gates/pipeline.js +167 -0
- package/dist/gates/scope-boundary.js +93 -0
- package/dist/gates/spec-completeness.js +109 -0
- package/dist/gates/todo-detection.js +205 -0
- package/dist/index.js +157 -151
- package/dist/parallel/parallel-manager.js +3 -3
- package/dist/policy/PolicyManager.js +51 -17
- package/dist/scaffold/claude-hooks.js +24 -1
- package/dist/scaffold/git-hooks.js +45 -102
- package/dist/scaffold/index.js +4 -3
- package/dist/session/session-manager.js +105 -14
- package/dist/sidecars/index.js +33 -0
- package/dist/sidecars/listeners.js +40 -0
- package/dist/sidecars/provenance-summary.js +238 -0
- package/dist/sidecars/quality-gaps.js +258 -0
- package/dist/sidecars/schema.js +149 -0
- package/dist/sidecars/spec-drift.js +151 -0
- package/dist/sidecars/waiver-draft.js +176 -0
- package/dist/templates/.caws/schemas/policy.schema.json +112 -0
- package/dist/templates/.caws/schemas/scope.schema.json +3 -3
- package/dist/templates/.caws/schemas/waivers.schema.json +96 -20
- package/dist/templates/.caws/schemas/working-spec.schema.json +264 -57
- package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/dist/templates/.caws/templates/working-spec.template.yml +10 -4
- package/dist/templates/.caws/tools/scope-guard.js +66 -15
- package/dist/templates/.claude/README.md +1 -1
- package/dist/templates/.claude/hooks/audit.sh +0 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/dist/templates/.claude/hooks/classify_command.py +592 -0
- package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/dist/templates/.claude/hooks/protected-paths.sh +39 -0
- package/dist/templates/.claude/hooks/quality-check.sh +23 -10
- package/dist/templates/.claude/hooks/scope-guard.sh +136 -55
- package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/dist/templates/.claude/hooks/session-log.sh +76 -3
- package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
- package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/dist/templates/.claude/hooks/worktree-write-guard.sh +97 -4
- package/dist/templates/.claude/settings.json +31 -0
- package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/dist/templates/.cursor/hooks/session-log.sh +924 -0
- package/dist/templates/.cursor/hooks.json +25 -0
- package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/dist/templates/.github/copilot-instructions.md +5 -5
- package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/dist/templates/.junie/guidelines.md +2 -2
- package/dist/templates/.vscode/settings.json +3 -1
- package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/dist/templates/CLAUDE.md +77 -8
- package/dist/templates/agents.md +50 -9
- package/dist/templates/docs/README.md +8 -7
- package/dist/templates/scripts/new_feature.sh +80 -0
- package/dist/test-analysis.js +43 -30
- package/dist/tool-loader.js +1 -1
- package/dist/utils/agent-session.js +202 -0
- package/dist/utils/detection.js +8 -2
- package/dist/utils/event-log.js +584 -0
- package/dist/utils/event-renderer.js +521 -0
- package/dist/utils/finalization.js +7 -6
- package/dist/utils/gitignore-updater.js +3 -0
- package/dist/utils/lifecycle-events.js +94 -0
- package/dist/utils/quality-gates-utils.js +29 -44
- package/dist/utils/schema-validator.js +50 -0
- package/dist/utils/spec-resolver.js +93 -21
- package/dist/utils/working-state.js +530 -0
- package/dist/validation/spec-validation.js +191 -31
- package/dist/waivers-manager.js +144 -6
- package/dist/worktree/worktree-manager.js +598 -95
- package/package.json +9 -8
- package/templates/.caws/schemas/policy.schema.json +112 -0
- package/templates/.caws/schemas/scope.schema.json +3 -3
- package/templates/.caws/schemas/waivers.schema.json +96 -20
- package/templates/.caws/schemas/working-spec.schema.json +264 -57
- package/templates/.caws/schemas/worktrees.schema.json +3 -1
- package/templates/.caws/templates/working-spec.template.yml +10 -4
- package/templates/.caws/tools/scope-guard.js +66 -15
- package/templates/.claude/README.md +1 -1
- package/templates/.claude/hooks/block-dangerous.sh +52 -11
- package/templates/.claude/hooks/classify_command.py +592 -0
- package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
- package/templates/.claude/hooks/protected-paths.sh +39 -0
- package/templates/.claude/hooks/quality-check.sh +23 -10
- package/templates/.claude/hooks/scope-guard.sh +136 -55
- package/templates/.claude/hooks/session-caws-status.sh +2 -2
- package/templates/.claude/hooks/session-log.sh +76 -3
- package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
- package/templates/.claude/hooks/test_classify_command.py +370 -0
- package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
- package/templates/.claude/hooks/worktree-guard.sh +2 -2
- package/templates/.claude/hooks/worktree-write-guard.sh +97 -4
- package/templates/.claude/settings.json +31 -0
- package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
- package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
- package/templates/.cursor/hooks/session-log.sh +924 -0
- package/templates/.cursor/hooks.json +25 -0
- package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
- package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
- package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
- package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
- package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
- package/templates/.github/copilot-instructions.md +5 -5
- package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
- package/templates/.junie/guidelines.md +2 -2
- package/templates/.vscode/settings.json +3 -1
- package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
- package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
- package/templates/CLAUDE.md +77 -8
- package/templates/{AGENTS.md → agents.md} +50 -9
- package/templates/docs/README.md +8 -7
- package/templates/scripts/new_feature.sh +80 -0
- package/dist/budget-derivation.d.ts +0 -74
- package/dist/budget-derivation.d.ts.map +0 -1
- package/dist/cicd-optimizer.d.ts +0 -142
- package/dist/cicd-optimizer.d.ts.map +0 -1
- package/dist/commands/archive.d.ts +0 -51
- package/dist/commands/archive.d.ts.map +0 -1
- package/dist/commands/burnup.d.ts +0 -6
- package/dist/commands/burnup.d.ts.map +0 -1
- package/dist/commands/diagnose.d.ts +0 -52
- package/dist/commands/diagnose.d.ts.map +0 -1
- package/dist/commands/evaluate.d.ts +0 -8
- package/dist/commands/evaluate.d.ts.map +0 -1
- package/dist/commands/init.d.ts +0 -5
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/iterate.d.ts +0 -8
- package/dist/commands/iterate.d.ts.map +0 -1
- package/dist/commands/mode.d.ts +0 -25
- package/dist/commands/mode.d.ts.map +0 -1
- package/dist/commands/parallel.d.ts +0 -7
- package/dist/commands/parallel.d.ts.map +0 -1
- package/dist/commands/plan.d.ts +0 -49
- package/dist/commands/plan.d.ts.map +0 -1
- package/dist/commands/provenance.d.ts +0 -32
- package/dist/commands/provenance.d.ts.map +0 -1
- package/dist/commands/quality-gates.d.ts +0 -6
- package/dist/commands/quality-gates.d.ts.map +0 -1
- package/dist/commands/quality-gates.js +0 -444
- package/dist/commands/quality-monitor.d.ts +0 -17
- package/dist/commands/quality-monitor.d.ts.map +0 -1
- package/dist/commands/session.d.ts +0 -7
- package/dist/commands/session.d.ts.map +0 -1
- package/dist/commands/specs.d.ts +0 -77
- package/dist/commands/specs.d.ts.map +0 -1
- package/dist/commands/status.d.ts +0 -44
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/templates.d.ts +0 -74
- package/dist/commands/templates.d.ts.map +0 -1
- package/dist/commands/tool.d.ts +0 -13
- package/dist/commands/tool.d.ts.map +0 -1
- package/dist/commands/troubleshoot.d.ts +0 -8
- package/dist/commands/troubleshoot.d.ts.map +0 -1
- package/dist/commands/troubleshoot.js +0 -104
- package/dist/commands/tutorial.d.ts +0 -55
- package/dist/commands/tutorial.d.ts.map +0 -1
- package/dist/commands/validate.d.ts +0 -15
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/waivers.d.ts +0 -8
- package/dist/commands/waivers.d.ts.map +0 -1
- package/dist/commands/workflow.d.ts +0 -85
- package/dist/commands/workflow.d.ts.map +0 -1
- package/dist/commands/worktree.d.ts +0 -7
- package/dist/commands/worktree.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -29
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/lite-scope.d.ts +0 -33
- package/dist/config/lite-scope.d.ts.map +0 -1
- package/dist/config/modes.d.ts +0 -264
- package/dist/config/modes.d.ts.map +0 -1
- package/dist/constants/spec-types.d.ts +0 -93
- package/dist/constants/spec-types.d.ts.map +0 -1
- package/dist/error-handler.d.ts +0 -151
- package/dist/error-handler.d.ts.map +0 -1
- package/dist/generators/jest-config-generator.d.ts +0 -32
- package/dist/generators/jest-config-generator.d.ts.map +0 -1
- package/dist/generators/jest-config.d.ts +0 -32
- package/dist/generators/jest-config.d.ts.map +0 -1
- package/dist/generators/jest-config.js +0 -242
- package/dist/generators/working-spec.d.ts +0 -13
- package/dist/generators/working-spec.d.ts.map +0 -1
- package/dist/index-new.d.ts +0 -5
- package/dist/index-new.d.ts.map +0 -1
- package/dist/index-new.js +0 -317
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.backup +0 -4711
- package/dist/minimal-cli.d.ts +0 -3
- package/dist/minimal-cli.d.ts.map +0 -1
- package/dist/parallel/parallel-manager.d.ts +0 -67
- package/dist/parallel/parallel-manager.d.ts.map +0 -1
- package/dist/policy/PolicyManager.d.ts +0 -104
- package/dist/policy/PolicyManager.d.ts.map +0 -1
- package/dist/scaffold/claude-hooks.d.ts +0 -28
- package/dist/scaffold/claude-hooks.d.ts.map +0 -1
- package/dist/scaffold/cursor-hooks.d.ts +0 -7
- package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
- package/dist/scaffold/git-hooks.d.ts +0 -38
- package/dist/scaffold/git-hooks.d.ts.map +0 -1
- package/dist/scaffold/index.d.ts +0 -17
- package/dist/scaffold/index.d.ts.map +0 -1
- package/dist/session/session-manager.d.ts +0 -94
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/spec/SpecFileManager.d.ts +0 -146
- package/dist/spec/SpecFileManager.d.ts.map +0 -1
- package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
- package/dist/templates/.github/copilot/instructions.md +0 -311
- package/dist/test-analysis.d.ts +0 -231
- package/dist/test-analysis.d.ts.map +0 -1
- package/dist/tool-interface.d.ts +0 -236
- package/dist/tool-interface.d.ts.map +0 -1
- package/dist/tool-loader.d.ts +0 -77
- package/dist/tool-loader.d.ts.map +0 -1
- package/dist/tool-validator.d.ts +0 -72
- package/dist/tool-validator.d.ts.map +0 -1
- package/dist/utils/async-utils.d.ts +0 -73
- package/dist/utils/async-utils.d.ts.map +0 -1
- package/dist/utils/command-wrapper.d.ts +0 -66
- package/dist/utils/command-wrapper.d.ts.map +0 -1
- package/dist/utils/detection.d.ts +0 -14
- package/dist/utils/detection.d.ts.map +0 -1
- package/dist/utils/error-categories.d.ts +0 -52
- package/dist/utils/error-categories.d.ts.map +0 -1
- package/dist/utils/finalization.d.ts +0 -17
- package/dist/utils/finalization.d.ts.map +0 -1
- package/dist/utils/git-lock.d.ts +0 -13
- package/dist/utils/git-lock.d.ts.map +0 -1
- package/dist/utils/gitignore-updater.d.ts +0 -39
- package/dist/utils/gitignore-updater.d.ts.map +0 -1
- package/dist/utils/ide-detection.d.ts +0 -89
- package/dist/utils/ide-detection.d.ts.map +0 -1
- package/dist/utils/project-analysis.d.ts +0 -34
- package/dist/utils/project-analysis.d.ts.map +0 -1
- package/dist/utils/promise-utils.d.ts +0 -30
- package/dist/utils/promise-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates-utils.d.ts +0 -49
- package/dist/utils/quality-gates-utils.d.ts.map +0 -1
- package/dist/utils/quality-gates.d.ts +0 -49
- package/dist/utils/quality-gates.d.ts.map +0 -1
- package/dist/utils/quality-gates.js +0 -402
- package/dist/utils/spec-resolver.d.ts +0 -80
- package/dist/utils/spec-resolver.d.ts.map +0 -1
- package/dist/utils/typescript-detector.d.ts +0 -66
- package/dist/utils/typescript-detector.d.ts.map +0 -1
- package/dist/utils/yaml-validation.d.ts +0 -32
- package/dist/utils/yaml-validation.d.ts.map +0 -1
- package/dist/validation/spec-validation.d.ts +0 -43
- package/dist/validation/spec-validation.d.ts.map +0 -1
- package/dist/waivers-manager.d.ts +0 -167
- package/dist/waivers-manager.d.ts.map +0 -1
- package/dist/worktree/worktree-manager.d.ts +0 -54
- package/dist/worktree/worktree-manager.d.ts.map +0 -1
package/dist/commands/archive.js
CHANGED
|
@@ -10,6 +10,7 @@ const yaml = require('js-yaml');
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const { execSync } = require('child_process');
|
|
12
12
|
const { safeAsync, outputResult } = require('../error-handler');
|
|
13
|
+
const { findProjectRoot } = require('../utils/detection');
|
|
13
14
|
|
|
14
15
|
// Import spec resolution system
|
|
15
16
|
const { resolveSpec } = require('../utils/spec-resolver');
|
|
@@ -20,7 +21,8 @@ const { resolveSpec } = require('../utils/spec-resolver');
|
|
|
20
21
|
* @returns {Promise<Object|null>} Change data or null
|
|
21
22
|
*/
|
|
22
23
|
async function loadChange(changeId) {
|
|
23
|
-
const
|
|
24
|
+
const projectRoot = findProjectRoot();
|
|
25
|
+
const changesDir = path.join(projectRoot, '.caws/changes');
|
|
24
26
|
const changePath = path.join(changesDir, changeId);
|
|
25
27
|
|
|
26
28
|
if (!(await fs.pathExists(changePath))) {
|
|
@@ -44,10 +46,11 @@ async function loadChange(changeId) {
|
|
|
44
46
|
path: changePath,
|
|
45
47
|
metadata,
|
|
46
48
|
workingSpec,
|
|
49
|
+
workingSpecPath: (await fs.pathExists(workingSpecPath)) ? workingSpecPath : null,
|
|
47
50
|
exists: true,
|
|
48
51
|
};
|
|
49
52
|
} catch (error) {
|
|
50
|
-
|
|
53
|
+
throw new Error(`Failed to load change '${changeId}': ${error.message}`);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -57,18 +60,24 @@ async function loadChange(changeId) {
|
|
|
57
60
|
* @returns {Promise<Object>} Validation result
|
|
58
61
|
*/
|
|
59
62
|
async function validateAcceptanceCriteria(workingSpec) {
|
|
60
|
-
|
|
63
|
+
const criteria = Array.isArray(workingSpec?.acceptance_criteria)
|
|
64
|
+
? workingSpec.acceptance_criteria
|
|
65
|
+
: Array.isArray(workingSpec?.acceptance)
|
|
66
|
+
? workingSpec.acceptance
|
|
67
|
+
: [];
|
|
68
|
+
|
|
69
|
+
if (!workingSpec || criteria.length === 0) {
|
|
61
70
|
return {
|
|
62
71
|
valid: false,
|
|
63
72
|
message: 'No acceptance criteria found in working spec',
|
|
64
73
|
};
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
const
|
|
76
|
+
const hasCompletionTracking = criteria.some((criterion) => criterion.completed !== undefined);
|
|
68
77
|
const incomplete = [];
|
|
69
78
|
|
|
70
79
|
for (const criterion of criteria) {
|
|
71
|
-
if (
|
|
80
|
+
if (criterion.completed === false) {
|
|
72
81
|
incomplete.push(criterion.id || 'unknown');
|
|
73
82
|
}
|
|
74
83
|
}
|
|
@@ -80,6 +89,13 @@ async function validateAcceptanceCriteria(workingSpec) {
|
|
|
80
89
|
};
|
|
81
90
|
}
|
|
82
91
|
|
|
92
|
+
if (!hasCompletionTracking) {
|
|
93
|
+
return {
|
|
94
|
+
valid: true,
|
|
95
|
+
message: `Acceptance criteria present (${criteria.length}); no explicit completion flags found`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
return {
|
|
84
100
|
valid: true,
|
|
85
101
|
message: `All ${criteria.length} acceptance criteria completed`,
|
|
@@ -212,8 +228,8 @@ async function validateQualityGates(_changeId) {
|
|
|
212
228
|
* @param {Object} change - Change data
|
|
213
229
|
* @returns {Promise<string>} Summary text
|
|
214
230
|
*/
|
|
215
|
-
async function generateChangeSummary(change) {
|
|
216
|
-
const {
|
|
231
|
+
async function generateChangeSummary(change, workingSpec) {
|
|
232
|
+
const { metadata } = change;
|
|
217
233
|
|
|
218
234
|
let summary = `# Change Summary: ${change.id}\n\n`;
|
|
219
235
|
|
|
@@ -267,7 +283,7 @@ async function archiveChange(change) {
|
|
|
267
283
|
* @param {Object} change - Change data
|
|
268
284
|
* @returns {Promise<void>}
|
|
269
285
|
*/
|
|
270
|
-
async function updateProvenance(change) {
|
|
286
|
+
async function updateProvenance(change, specSelection) {
|
|
271
287
|
const provenanceDir = '.caws/provenance';
|
|
272
288
|
const chainPath = path.join(provenanceDir, 'chain.json');
|
|
273
289
|
|
|
@@ -284,8 +300,11 @@ async function updateProvenance(change) {
|
|
|
284
300
|
action: 'change_completed',
|
|
285
301
|
change_id: change.id,
|
|
286
302
|
metadata: {
|
|
287
|
-
title: change.workingSpec?.title,
|
|
288
|
-
risk_tier: change.workingSpec?.risk_tier,
|
|
303
|
+
title: specSelection?.spec?.title || change.workingSpec?.title,
|
|
304
|
+
risk_tier: specSelection?.spec?.risk_tier || change.workingSpec?.risk_tier,
|
|
305
|
+
spec_id: specSelection?.spec?.id || change.workingSpec?.id || null,
|
|
306
|
+
spec_path: specSelection?.path || change.workingSpecPath || null,
|
|
307
|
+
spec_type: specSelection?.type || (change.workingSpecPath ? 'change-snapshot' : null),
|
|
289
308
|
files_changed: change.metadata?.files_changed || 0,
|
|
290
309
|
lines_added: change.metadata?.lines_added || 0,
|
|
291
310
|
lines_removed: change.metadata?.lines_removed || 0,
|
|
@@ -309,10 +328,20 @@ async function updateProvenance(change) {
|
|
|
309
328
|
* @param {Object} validation - Validation result
|
|
310
329
|
* @param {Object} qualityGates - Quality gates result
|
|
311
330
|
*/
|
|
312
|
-
function displayArchiveResults(change, validation, qualityGates) {
|
|
331
|
+
function displayArchiveResults(change, validation, qualityGates, specSelection) {
|
|
313
332
|
console.log(chalk.bold.cyan(`\nArchiving Change: ${change.id}`));
|
|
314
333
|
console.log(chalk.cyan('==============================================\n'));
|
|
315
334
|
|
|
335
|
+
if (specSelection?.spec) {
|
|
336
|
+
console.log(chalk.blue('Spec Context:'));
|
|
337
|
+
console.log(
|
|
338
|
+
chalk.gray(
|
|
339
|
+
` ${specSelection.spec.id || 'unknown'} (${specSelection.type}) -> ${specSelection.path}`
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
console.log('');
|
|
343
|
+
}
|
|
344
|
+
|
|
316
345
|
// Validation status
|
|
317
346
|
if (validation.valid) {
|
|
318
347
|
console.log(chalk.green('Acceptance Criteria'));
|
|
@@ -363,22 +392,27 @@ async function archiveCommand(changeId, options = {}) {
|
|
|
363
392
|
}
|
|
364
393
|
|
|
365
394
|
// Resolve spec using priority system
|
|
366
|
-
let
|
|
367
|
-
if (
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
395
|
+
let specSelection = null;
|
|
396
|
+
if (options.specId || options.specFile) {
|
|
397
|
+
specSelection = await resolveSpec({
|
|
398
|
+
specId: options.specId,
|
|
399
|
+
specFile: options.specFile,
|
|
400
|
+
warnLegacy: false,
|
|
401
|
+
});
|
|
402
|
+
} else if (change.workingSpec) {
|
|
403
|
+
specSelection = {
|
|
404
|
+
path: change.workingSpecPath || path.join(change.path, 'working-spec.yaml'),
|
|
405
|
+
type: 'change-snapshot',
|
|
406
|
+
spec: change.workingSpec,
|
|
407
|
+
};
|
|
408
|
+
} else {
|
|
409
|
+
specSelection = await resolveSpec({
|
|
410
|
+
warnLegacy: false,
|
|
411
|
+
});
|
|
380
412
|
}
|
|
381
413
|
|
|
414
|
+
const workingSpec = specSelection.spec;
|
|
415
|
+
|
|
382
416
|
// Validate acceptance criteria
|
|
383
417
|
const validation = await validateAcceptanceCriteria(workingSpec);
|
|
384
418
|
|
|
@@ -386,7 +420,7 @@ async function archiveCommand(changeId, options = {}) {
|
|
|
386
420
|
const qualityGates = await validateQualityGates(changeId);
|
|
387
421
|
|
|
388
422
|
// Display results
|
|
389
|
-
displayArchiveResults(change, validation, qualityGates);
|
|
423
|
+
displayArchiveResults(change, validation, qualityGates, specSelection);
|
|
390
424
|
|
|
391
425
|
// Check if we should proceed with archival
|
|
392
426
|
if (!validation.valid) {
|
|
@@ -423,7 +457,7 @@ async function archiveCommand(changeId, options = {}) {
|
|
|
423
457
|
change.metadata.archived = true;
|
|
424
458
|
|
|
425
459
|
// Generate and save summary
|
|
426
|
-
const summary = await generateChangeSummary(change);
|
|
460
|
+
const summary = await generateChangeSummary(change, workingSpec);
|
|
427
461
|
const summaryPath = path.join(change.path, 'archive-summary.md');
|
|
428
462
|
await fs.writeFile(summaryPath, summary);
|
|
429
463
|
|
|
@@ -431,7 +465,7 @@ async function archiveCommand(changeId, options = {}) {
|
|
|
431
465
|
await archiveChange(change);
|
|
432
466
|
|
|
433
467
|
// Update provenance
|
|
434
|
-
await updateProvenance(change);
|
|
468
|
+
await updateProvenance(change, specSelection);
|
|
435
469
|
|
|
436
470
|
console.log(chalk.green(`\nSuccessfully archived change: ${changeId}`));
|
|
437
471
|
|
|
@@ -439,6 +473,11 @@ async function archiveCommand(changeId, options = {}) {
|
|
|
439
473
|
command: 'archive',
|
|
440
474
|
change: changeId,
|
|
441
475
|
archived: true,
|
|
476
|
+
specSelection: {
|
|
477
|
+
id: workingSpec?.id || null,
|
|
478
|
+
path: specSelection?.path || null,
|
|
479
|
+
type: specSelection?.type || null,
|
|
480
|
+
},
|
|
442
481
|
validation: validation.valid,
|
|
443
482
|
qualityGates: qualityGates.valid,
|
|
444
483
|
summary: summary,
|
package/dist/commands/burnup.js
CHANGED
|
@@ -11,6 +11,7 @@ const chalk = require('chalk');
|
|
|
11
11
|
const { execSync } = require('child_process');
|
|
12
12
|
|
|
13
13
|
const { deriveBudget, generateBurnupReport } = require('../budget-derivation');
|
|
14
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Get actual git change statistics from the repository
|
|
@@ -86,24 +87,32 @@ function getGitChangeStats(specDir) {
|
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Burn-up command handler
|
|
89
|
-
* @param {string} specFile - Path to spec file
|
|
90
|
+
* @param {string} specFile - Path to spec file (positional, optional)
|
|
91
|
+
* @param {object} options - Command options including --spec-id
|
|
90
92
|
*/
|
|
91
|
-
async function burnupCommand(specFile) {
|
|
93
|
+
async function burnupCommand(specFile, options = {}) {
|
|
92
94
|
try {
|
|
93
|
-
let specPath
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
let specPath;
|
|
96
|
+
let spec;
|
|
97
|
+
|
|
98
|
+
// Resolve spec: explicit file > --spec-id > resolver default
|
|
99
|
+
if (specFile) {
|
|
100
|
+
specPath = specFile;
|
|
101
|
+
if (!fs.existsSync(specPath)) {
|
|
102
|
+
console.error(chalk.red(`Spec file not found: ${specPath}`));
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
|
|
106
|
+
} else {
|
|
107
|
+
const resolved = await resolveSpec({ specId: options.specId });
|
|
108
|
+
specPath = resolved.path;
|
|
109
|
+
spec = resolved.spec;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
const specContent = fs.readFileSync(specPath, 'utf8');
|
|
101
|
-
const spec = yaml.load(specContent);
|
|
102
|
-
|
|
103
112
|
console.log(chalk.cyan('Generating CAWS budget burn-up report...'));
|
|
104
113
|
|
|
105
114
|
// Derive budget
|
|
106
|
-
const derivedBudget = deriveBudget(spec, path.dirname(specPath));
|
|
115
|
+
const derivedBudget = await deriveBudget(spec, path.dirname(specPath));
|
|
107
116
|
|
|
108
117
|
// Get actual git change statistics
|
|
109
118
|
const gitStats = getGitChangeStats(path.dirname(specPath));
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs-extra');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const yaml = require('js-yaml');
|
|
10
9
|
const chalk = require('chalk');
|
|
10
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
11
11
|
|
|
12
12
|
// Import utilities
|
|
13
13
|
const { checkTypeScriptTestConfig } = require('../utils/typescript-detector');
|
|
@@ -17,29 +17,23 @@ const { configureJestForTypeScript } = require('../generators/jest-config-genera
|
|
|
17
17
|
* Health check: Working spec validity
|
|
18
18
|
* @returns {Promise<Object>} Check result
|
|
19
19
|
*/
|
|
20
|
-
async function checkWorkingSpec() {
|
|
21
|
-
const specPath = '.caws/working-spec.yaml';
|
|
22
|
-
|
|
23
|
-
if (!(await fs.pathExists(specPath))) {
|
|
24
|
-
return {
|
|
25
|
-
passed: false,
|
|
26
|
-
severity: 'high',
|
|
27
|
-
message: 'Working spec not found',
|
|
28
|
-
fix: 'Initialize CAWS: caws init .',
|
|
29
|
-
autoFixable: false,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
20
|
+
async function checkWorkingSpec(options = {}) {
|
|
33
21
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
22
|
+
const resolved = await resolveSpec({
|
|
23
|
+
specId: options.specId,
|
|
24
|
+
specFile: options.spec,
|
|
25
|
+
warnLegacy: false,
|
|
26
|
+
interactive: false,
|
|
27
|
+
});
|
|
28
|
+
const spec = resolved.spec;
|
|
29
|
+
const specPath = path.relative(process.cwd(), resolved.path);
|
|
36
30
|
|
|
37
31
|
// Basic validation
|
|
38
32
|
if (!spec.id || !spec.title || !spec.risk_tier) {
|
|
39
33
|
return {
|
|
40
34
|
passed: false,
|
|
41
35
|
severity: 'high',
|
|
42
|
-
message:
|
|
36
|
+
message: `Spec missing required fields (${specPath})`,
|
|
43
37
|
fix: 'Run: caws validate for details',
|
|
44
38
|
autoFixable: false,
|
|
45
39
|
};
|
|
@@ -47,13 +41,31 @@ async function checkWorkingSpec() {
|
|
|
47
41
|
|
|
48
42
|
return {
|
|
49
43
|
passed: true,
|
|
50
|
-
message:
|
|
44
|
+
message: `Spec is valid (${specPath})`,
|
|
51
45
|
};
|
|
52
46
|
} catch (error) {
|
|
47
|
+
if (error.message === 'Spec ID required when multiple specs exist' && !options.specId) {
|
|
48
|
+
return {
|
|
49
|
+
passed: false,
|
|
50
|
+
severity: 'high',
|
|
51
|
+
message: 'Multiple specs detected, but no --spec-id was provided',
|
|
52
|
+
fix: 'Run: caws diagnose --spec-id <id>',
|
|
53
|
+
autoFixable: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (error.message.includes('No CAWS spec found')) {
|
|
57
|
+
return {
|
|
58
|
+
passed: false,
|
|
59
|
+
severity: 'high',
|
|
60
|
+
message: 'No CAWS spec found',
|
|
61
|
+
fix: 'Initialize CAWS: caws init . or create a feature spec with caws specs create <id>',
|
|
62
|
+
autoFixable: false,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
53
65
|
return {
|
|
54
66
|
passed: false,
|
|
55
67
|
severity: 'high',
|
|
56
|
-
message: `
|
|
68
|
+
message: `Spec has errors: ${error.message}`,
|
|
57
69
|
fix: 'Run: caws validate for details',
|
|
58
70
|
autoFixable: false,
|
|
59
71
|
};
|
|
@@ -285,9 +297,9 @@ async function checkCAWSTools() {
|
|
|
285
297
|
* Run all health checks
|
|
286
298
|
* @returns {Promise<Object>} Diagnosis results
|
|
287
299
|
*/
|
|
288
|
-
async function runDiagnosis() {
|
|
300
|
+
async function runDiagnosis(options = {}) {
|
|
289
301
|
const checks = [
|
|
290
|
-
{ name: 'Working spec validity', fn: checkWorkingSpec },
|
|
302
|
+
{ name: 'Working spec validity', fn: () => checkWorkingSpec(options) },
|
|
291
303
|
{ name: 'Git repository', fn: checkGitSetup },
|
|
292
304
|
{ name: 'Git hooks', fn: checkGitHooks },
|
|
293
305
|
{ name: 'TypeScript configuration', fn: checkTypeScriptConfig },
|
|
@@ -447,7 +459,7 @@ async function applyAutoFixes(results) {
|
|
|
447
459
|
async function diagnoseCommand(options = {}) {
|
|
448
460
|
try {
|
|
449
461
|
// Run all health checks
|
|
450
|
-
const results = await runDiagnosis();
|
|
462
|
+
const results = await runDiagnosis(options);
|
|
451
463
|
|
|
452
464
|
// Display results
|
|
453
465
|
displayResults(results);
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* @author @darianrosebrook
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const fs = require('fs');
|
|
11
10
|
const path = require('path');
|
|
12
|
-
const yaml = require('js-yaml');
|
|
13
11
|
const chalk = require('chalk');
|
|
14
12
|
const { initializeGlobalSetup } = require('../config');
|
|
13
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
14
|
+
const { recordEvaluation } = require('../utils/working-state');
|
|
15
|
+
const { appendEvent } = require('../utils/event-log');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Evaluate command handler
|
|
@@ -19,30 +20,29 @@ const { initializeGlobalSetup } = require('../config');
|
|
|
19
20
|
* @param {string} specFile - Path to working spec file
|
|
20
21
|
* @param {object} options - Command options
|
|
21
22
|
*/
|
|
22
|
-
async function evaluateCommand(specFile
|
|
23
|
+
async function evaluateCommand(specFile, options = {}) {
|
|
23
24
|
try {
|
|
24
25
|
console.log('Detecting CAWS setup...');
|
|
25
26
|
const setup = initializeGlobalSetup();
|
|
26
27
|
|
|
27
28
|
if (setup.hasWorkingSpec) {
|
|
28
|
-
console.log(`Detected ${setup.
|
|
29
|
+
console.log(`Detected ${setup.type} CAWS setup`);
|
|
29
30
|
console.log(` Capabilities: ${setup.capabilities.join(', ')}`);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const specContent = fs.readFileSync(specPath, 'utf8');
|
|
42
|
-
const spec = yaml.load(specContent);
|
|
33
|
+
const resolved = await resolveSpec({
|
|
34
|
+
specId: options.specId,
|
|
35
|
+
specFile: specFile || undefined,
|
|
36
|
+
warnLegacy: false,
|
|
37
|
+
interactive: false,
|
|
38
|
+
});
|
|
39
|
+
const specPath = resolved.path;
|
|
40
|
+
const spec = resolved.spec;
|
|
43
41
|
|
|
44
42
|
console.log(chalk.blue('\nEvaluating CAWS Quality Standards\n'));
|
|
45
43
|
console.log('-'.repeat(60));
|
|
44
|
+
console.log(chalk.gray(`Spec: ${path.relative(process.cwd(), specPath)}`));
|
|
45
|
+
console.log(chalk.gray(`Type: ${resolved.type}`));
|
|
46
46
|
|
|
47
47
|
// Evaluation results
|
|
48
48
|
const results = {
|
|
@@ -203,6 +203,32 @@ async function evaluateCommand(specFile = '.caws/working-spec.yaml', options = {
|
|
|
203
203
|
? 'D'
|
|
204
204
|
: 'F';
|
|
205
205
|
|
|
206
|
+
// Record to working state (Phase 1 dual-write: state layer + event log)
|
|
207
|
+
const checksPassed = results.checks.filter(c => c.status === 'pass').length;
|
|
208
|
+
const evaluationPayload = {
|
|
209
|
+
score: results.score,
|
|
210
|
+
max_score: results.maxScore,
|
|
211
|
+
percentage,
|
|
212
|
+
grade,
|
|
213
|
+
checks_passed: checksPassed,
|
|
214
|
+
checks_total: results.checks.length,
|
|
215
|
+
};
|
|
216
|
+
// CAWSFIX-02: guard recordEvaluation with `spec && spec.id` check to
|
|
217
|
+
// prevent the .caws/state/undefined.json bug class. Matches the pattern
|
|
218
|
+
// gates.js already uses and the appendEvent call below.
|
|
219
|
+
if (spec && spec.id) {
|
|
220
|
+
try {
|
|
221
|
+
recordEvaluation(spec.id, evaluationPayload);
|
|
222
|
+
} catch { /* non-fatal */ }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// EVLOG-001: emit evaluation_completed event alongside state write.
|
|
226
|
+
if (spec && spec.id) {
|
|
227
|
+
await appendEvent(
|
|
228
|
+
{ actor: 'cli', event: 'evaluation_completed', spec_id: spec.id, data: evaluationPayload }
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
206
232
|
console.log('\n' + '-'.repeat(60));
|
|
207
233
|
console.log(
|
|
208
234
|
chalk.bold(
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Unified quality gates CLI command
|
|
3
|
+
* Delegates to the v2 gate pipeline (src/gates/pipeline.js) for evaluation
|
|
4
|
+
* and formatting (src/gates/format.js) for output.
|
|
5
|
+
* @author @darianrosebrook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { evaluateGates } = require('../gates/pipeline');
|
|
9
|
+
const { formatText, formatJson, formatEnrichedText } = require('../gates/format');
|
|
10
|
+
const { enrichGateResults } = require('../gates/feedback');
|
|
11
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
12
|
+
const { commandWrapper } = require('../utils/command-wrapper');
|
|
13
|
+
// EVLOG-002 Phase 2 read flip: gates still writes via recordGates (state layer)
|
|
14
|
+
// AND emits a gates_evaluated event (from Phase 1), but the feedback-enrichment
|
|
15
|
+
// READ at line ~116 now goes through the event log renderer instead of loadState.
|
|
16
|
+
// The write path is deliberately unchanged in Phase 2 — only reads flip.
|
|
17
|
+
const { recordGates } = require('../utils/working-state');
|
|
18
|
+
const { loadStateFromEvents } = require('../utils/event-renderer');
|
|
19
|
+
const { appendEvent } = require('../utils/event-log');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Run quality gates via the v2 pipeline
|
|
23
|
+
* @param {Object} options - Command options
|
|
24
|
+
* @param {string} [options.context] - Execution context (cli, commit, edit)
|
|
25
|
+
* @param {string} [options.specId] - Target spec ID
|
|
26
|
+
* @param {string} [options.specFile] - Explicit spec file path
|
|
27
|
+
* @param {string} [options.file] - Single file to check (for edit context)
|
|
28
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
29
|
+
* @param {string} [options.format] - Output format (text, json)
|
|
30
|
+
* @param {boolean} [options.quiet] - Minimal output
|
|
31
|
+
*/
|
|
32
|
+
async function gatesCommand(options = {}) {
|
|
33
|
+
return commandWrapper(
|
|
34
|
+
async () => {
|
|
35
|
+
const projectRoot = process.cwd();
|
|
36
|
+
const context = options.context || 'cli';
|
|
37
|
+
|
|
38
|
+
// Resolve spec (working-spec or feature spec)
|
|
39
|
+
let spec = null;
|
|
40
|
+
try {
|
|
41
|
+
const resolved = await resolveSpec({
|
|
42
|
+
specId: options.specId,
|
|
43
|
+
specFile: options.specFile,
|
|
44
|
+
warnLegacy: false,
|
|
45
|
+
interactive: false,
|
|
46
|
+
});
|
|
47
|
+
spec = resolved.spec;
|
|
48
|
+
} catch {
|
|
49
|
+
// No spec available — gates that need it will handle gracefully
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Get file list based on context
|
|
53
|
+
let stagedFiles = [];
|
|
54
|
+
const { execSync } = require('child_process');
|
|
55
|
+
if (context === 'commit') {
|
|
56
|
+
try {
|
|
57
|
+
stagedFiles = execSync('git diff --cached --name-only', {
|
|
58
|
+
cwd: projectRoot,
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
61
|
+
})
|
|
62
|
+
.trim()
|
|
63
|
+
.split('\n')
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
} catch {
|
|
66
|
+
/* no staged files */
|
|
67
|
+
}
|
|
68
|
+
} else if (options.file) {
|
|
69
|
+
stagedFiles = [options.file];
|
|
70
|
+
} else if (context === 'cli') {
|
|
71
|
+
// For CLI context, use all tracked files so gates can provide meaningful analysis
|
|
72
|
+
try {
|
|
73
|
+
stagedFiles = execSync('git ls-files', {
|
|
74
|
+
cwd: projectRoot,
|
|
75
|
+
encoding: 'utf8',
|
|
76
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
77
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
78
|
+
})
|
|
79
|
+
.trim()
|
|
80
|
+
.split('\n')
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
} catch {
|
|
83
|
+
/* not a git repo or git unavailable */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const report = await evaluateGates({ projectRoot, stagedFiles, spec, context });
|
|
88
|
+
|
|
89
|
+
// Record to working state (Phase 1 dual-write: state layer + event log)
|
|
90
|
+
if (spec && spec.id) {
|
|
91
|
+
try { recordGates(spec.id, report, context, projectRoot); } catch { /* non-fatal */ }
|
|
92
|
+
|
|
93
|
+
// EVLOG-001: emit gates_evaluated event alongside state write.
|
|
94
|
+
// Payload shape mirrors the `gates` field produced by recordGates.
|
|
95
|
+
await appendEvent(
|
|
96
|
+
{
|
|
97
|
+
actor: 'cli',
|
|
98
|
+
event: 'gates_evaluated',
|
|
99
|
+
spec_id: spec.id,
|
|
100
|
+
data: {
|
|
101
|
+
context,
|
|
102
|
+
passed: report.passed,
|
|
103
|
+
summary: report.summary || {},
|
|
104
|
+
gates: (report.gates || []).map((g) => ({
|
|
105
|
+
name: g.name,
|
|
106
|
+
status: g.status,
|
|
107
|
+
mode: g.mode,
|
|
108
|
+
})),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{ projectRoot }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (options.json || options.format === 'json') {
|
|
116
|
+
console.log(formatJson(report));
|
|
117
|
+
} else if (!options.quiet) {
|
|
118
|
+
// Enrich feedback on failure or --verbose (EVLOG-002: from event log)
|
|
119
|
+
if (!report.passed || options.verbose) {
|
|
120
|
+
try {
|
|
121
|
+
const state = spec?.id ? loadStateFromEvents(spec.id, { projectRoot }) : null;
|
|
122
|
+
const enrichments = enrichGateResults(report, { spec, state, projectRoot });
|
|
123
|
+
if (enrichments.size > 0) {
|
|
124
|
+
console.log(formatEnrichedText(report, enrichments));
|
|
125
|
+
} else {
|
|
126
|
+
console.log(formatText(report));
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
console.log(formatText(report));
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
console.log(formatText(report));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Exit with appropriate code
|
|
137
|
+
if (!report.passed) {
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
commandName: 'gates',
|
|
143
|
+
context: { options },
|
|
144
|
+
exitOnError: true,
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = { gatesCommand };
|