@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/status.js
CHANGED
|
@@ -11,6 +11,12 @@ const chalk = require('chalk');
|
|
|
11
11
|
// child_process removed - execSync no longer used directly
|
|
12
12
|
const { safeAsync, outputResult } = require('../error-handler');
|
|
13
13
|
const { parallel } = require('../utils/async-utils');
|
|
14
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
15
|
+
// EVLOG-002 Phase 2 read flip: status reads working state from the event log
|
|
16
|
+
// via the pure renderer. loadStateFromEvents matches loadState's null contract
|
|
17
|
+
// exactly, so the `ws && ws.phase !== 'not-started'` guard and the
|
|
18
|
+
// `loadState(id) || null` coalesce below stay semantically correct.
|
|
19
|
+
const { loadStateFromEvents } = require('../utils/event-renderer');
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* Load working specification (legacy single file approach)
|
|
@@ -30,6 +36,41 @@ async function loadWorkingSpec(specPath = '.caws/working-spec.yaml') {
|
|
|
30
36
|
}
|
|
31
37
|
}
|
|
32
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the primary spec for status display when explicitly targeted
|
|
41
|
+
* or when the resolver can identify a single canonical spec.
|
|
42
|
+
* Returns null when no spec can be auto-selected.
|
|
43
|
+
* @param {Object} options
|
|
44
|
+
* @returns {Promise<Object|null>}
|
|
45
|
+
*/
|
|
46
|
+
async function loadResolvedStatusSpec(options = {}) {
|
|
47
|
+
try {
|
|
48
|
+
const resolved = await resolveSpec({
|
|
49
|
+
specId: options.specId,
|
|
50
|
+
specFile: options.spec,
|
|
51
|
+
warnLegacy: false,
|
|
52
|
+
interactive: false,
|
|
53
|
+
quiet: Boolean(options.json),
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
...resolved,
|
|
57
|
+
selected: true,
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (
|
|
61
|
+
error.message === 'Spec ID required when multiple specs exist' &&
|
|
62
|
+
!options.specId &&
|
|
63
|
+
!options.spec
|
|
64
|
+
) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (error.message.includes('No CAWS spec found')) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
33
74
|
/**
|
|
34
75
|
* Load specs from the new multi-spec system
|
|
35
76
|
* @returns {Promise<Array>} Array of spec objects
|
|
@@ -317,7 +358,7 @@ function getTimeSince(timestamp) {
|
|
|
317
358
|
* @param {Object} data - Status data
|
|
318
359
|
*/
|
|
319
360
|
function displayStatus(data) {
|
|
320
|
-
const { spec, hooks, provenance, waivers, gates } = data;
|
|
361
|
+
const { spec, specSelection, hooks, provenance, waivers, gates } = data;
|
|
321
362
|
|
|
322
363
|
console.log(chalk.bold.cyan('\nCAWS Project Status'));
|
|
323
364
|
console.log(chalk.cyan('==============================================\n'));
|
|
@@ -327,6 +368,9 @@ function displayStatus(data) {
|
|
|
327
368
|
console.log(chalk.green('Working Spec'));
|
|
328
369
|
console.log(chalk.gray(` ID: ${spec.id} | Tier: ${spec.risk_tier} | Mode: ${spec.mode}`));
|
|
329
370
|
console.log(chalk.gray(` Title: ${spec.title}`));
|
|
371
|
+
if (specSelection?.specType) {
|
|
372
|
+
console.log(chalk.gray(` Source: ${specSelection.specType} (${specSelection.specPath})`));
|
|
373
|
+
}
|
|
330
374
|
} else {
|
|
331
375
|
console.log(chalk.red('Working Spec'));
|
|
332
376
|
console.log(chalk.gray(' No working spec found'));
|
|
@@ -335,6 +379,47 @@ function displayStatus(data) {
|
|
|
335
379
|
|
|
336
380
|
console.log('');
|
|
337
381
|
|
|
382
|
+
// Working State (EVLOG-002: from event log)
|
|
383
|
+
if (spec && spec.id) {
|
|
384
|
+
let ws = null;
|
|
385
|
+
try { ws = loadStateFromEvents(spec.id); } catch { /* non-fatal */ }
|
|
386
|
+
if (ws && ws.phase !== 'not-started') {
|
|
387
|
+
const phaseLabels = {
|
|
388
|
+
'not-started': 'Not Started',
|
|
389
|
+
'spec-authoring': 'Spec Authoring',
|
|
390
|
+
'implementation': 'Implementation',
|
|
391
|
+
'verification': 'Verification',
|
|
392
|
+
'complete': 'Complete',
|
|
393
|
+
};
|
|
394
|
+
console.log(chalk.green('Working State'));
|
|
395
|
+
console.log(chalk.gray(` Phase: ${phaseLabels[ws.phase] || ws.phase}`));
|
|
396
|
+
if (ws.validation) {
|
|
397
|
+
const vIcon = ws.validation.passed ? chalk.green('pass') : chalk.red('fail');
|
|
398
|
+
console.log(chalk.gray(` Validation: ${vIcon}${ws.validation.grade ? ` (Grade ${ws.validation.grade})` : ''}`));
|
|
399
|
+
}
|
|
400
|
+
if (ws.evaluation) {
|
|
401
|
+
console.log(chalk.gray(` Evaluation: ${ws.evaluation.percentage}% (Grade ${ws.evaluation.grade})`));
|
|
402
|
+
}
|
|
403
|
+
if (ws.gates) {
|
|
404
|
+
const gIcon = ws.gates.passed ? chalk.green('pass') : chalk.red('blocked');
|
|
405
|
+
const s = ws.gates.summary;
|
|
406
|
+
console.log(chalk.gray(` Gates: ${gIcon} (${s.passed} pass, ${s.blocked} blocked, ${s.warned} warned)`));
|
|
407
|
+
}
|
|
408
|
+
if (ws.acceptance_criteria) {
|
|
409
|
+
const ac = ws.acceptance_criteria;
|
|
410
|
+
console.log(chalk.gray(` ACs: ${ac.pass}/${ac.total} pass, ${ac.fail} fail, ${ac.unchecked} unchecked`));
|
|
411
|
+
}
|
|
412
|
+
if (ws.files_touched && ws.files_touched.length > 0) {
|
|
413
|
+
console.log(chalk.gray(` Files touched: ${ws.files_touched.length}`));
|
|
414
|
+
}
|
|
415
|
+
if (ws.blockers && ws.blockers.length > 0) {
|
|
416
|
+
console.log(chalk.red(` Blockers: ${ws.blockers.length}`));
|
|
417
|
+
ws.blockers.forEach(b => console.log(chalk.red(` - ${b.message}`)));
|
|
418
|
+
}
|
|
419
|
+
console.log('');
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
338
423
|
// Git Hooks Status
|
|
339
424
|
if (hooks.installed) {
|
|
340
425
|
console.log(chalk.green(`Git Hooks`));
|
|
@@ -894,14 +979,23 @@ async function statusCommand(options = {}) {
|
|
|
894
979
|
const currentMode = await modes.getCurrentMode();
|
|
895
980
|
|
|
896
981
|
// Load all status data in parallel for better performance
|
|
897
|
-
const [
|
|
898
|
-
(
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
982
|
+
const [resolvedSpec, specs, hooks, provenance, waivers, gates] = await parallel([
|
|
983
|
+
loadResolvedStatusSpec(options),
|
|
984
|
+
loadSpecsFromMultiSpec(),
|
|
985
|
+
checkGitHooks(),
|
|
986
|
+
loadProvenanceChain(),
|
|
987
|
+
loadWaiverStatus(),
|
|
988
|
+
checkQualityGates(),
|
|
904
989
|
]);
|
|
990
|
+
const spec = resolvedSpec?.spec || null;
|
|
991
|
+
const specSelection = resolvedSpec
|
|
992
|
+
? {
|
|
993
|
+
specPath: resolvedSpec.path
|
|
994
|
+
? path.relative(process.cwd(), resolvedSpec.path)
|
|
995
|
+
: null,
|
|
996
|
+
specType: resolvedSpec.type,
|
|
997
|
+
}
|
|
998
|
+
: null;
|
|
905
999
|
|
|
906
1000
|
// Display status (visual mode if requested)
|
|
907
1001
|
if (options.visual || options.json) {
|
|
@@ -932,6 +1026,8 @@ async function statusCommand(options = {}) {
|
|
|
932
1026
|
title: spec.title,
|
|
933
1027
|
riskTier: spec.risk_tier,
|
|
934
1028
|
mode: spec.mode,
|
|
1029
|
+
specType: specSelection?.specType || null,
|
|
1030
|
+
specPath: specSelection?.specPath || null,
|
|
935
1031
|
acceptanceCriteria: spec.acceptance_criteria?.length || 0,
|
|
936
1032
|
completedCriteria:
|
|
937
1033
|
spec.acceptance_criteria?.filter((c) => c.completed).length || 0,
|
|
@@ -960,8 +1056,10 @@ async function statusCommand(options = {}) {
|
|
|
960
1056
|
passed: gates.passed,
|
|
961
1057
|
message: gates.message,
|
|
962
1058
|
},
|
|
1059
|
+
workingState: spec && spec.id ? (loadStateFromEvents(spec.id) || null) : null,
|
|
963
1060
|
overallProgress: calculateOverallProgress({
|
|
964
1061
|
spec,
|
|
1062
|
+
specSelection,
|
|
965
1063
|
specs,
|
|
966
1064
|
hooks,
|
|
967
1065
|
provenance,
|
|
@@ -971,6 +1069,7 @@ async function statusCommand(options = {}) {
|
|
|
971
1069
|
};
|
|
972
1070
|
|
|
973
1071
|
console.log(JSON.stringify(result, null, 2));
|
|
1072
|
+
return result;
|
|
974
1073
|
} else {
|
|
975
1074
|
// Visual output
|
|
976
1075
|
await displayVisualStatus(
|
|
@@ -989,6 +1088,7 @@ async function statusCommand(options = {}) {
|
|
|
989
1088
|
// Original text-based output
|
|
990
1089
|
displayStatus({
|
|
991
1090
|
spec,
|
|
1091
|
+
specSelection,
|
|
992
1092
|
hooks,
|
|
993
1093
|
provenance,
|
|
994
1094
|
waivers,
|
|
@@ -996,6 +1096,13 @@ async function statusCommand(options = {}) {
|
|
|
996
1096
|
});
|
|
997
1097
|
}
|
|
998
1098
|
|
|
1099
|
+
if (options.json) {
|
|
1100
|
+
return {
|
|
1101
|
+
command: 'status',
|
|
1102
|
+
mode: 'json',
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
999
1106
|
const result = outputResult({
|
|
1000
1107
|
command: 'status',
|
|
1001
1108
|
mode: options.visual ? 'visual' : options.json ? 'json' : 'text',
|
|
@@ -1027,13 +1134,14 @@ async function statusCommand(options = {}) {
|
|
|
1027
1134
|
return result;
|
|
1028
1135
|
},
|
|
1029
1136
|
'status check',
|
|
1030
|
-
|
|
1137
|
+
!options.json
|
|
1031
1138
|
);
|
|
1032
1139
|
}
|
|
1033
1140
|
|
|
1034
1141
|
module.exports = {
|
|
1035
1142
|
statusCommand,
|
|
1036
1143
|
loadWorkingSpec,
|
|
1144
|
+
loadResolvedStatusSpec,
|
|
1037
1145
|
checkGitHooks,
|
|
1038
1146
|
loadProvenanceChain,
|
|
1039
1147
|
loadWaiverStatus,
|
|
@@ -53,14 +53,6 @@ const BUILTIN_TEMPLATES = {
|
|
|
53
53
|
features: ['React', 'TypeScript', 'Storybook', 'Jest', 'Publishing'],
|
|
54
54
|
path: 'templates/react/component-library',
|
|
55
55
|
},
|
|
56
|
-
'vscode-extension': {
|
|
57
|
-
name: 'VS Code Extension',
|
|
58
|
-
description: 'VS Code extension with TypeScript',
|
|
59
|
-
category: 'Extension',
|
|
60
|
-
tier: 2,
|
|
61
|
-
features: ['TypeScript', 'VS Code API', 'Jest', 'Publishing'],
|
|
62
|
-
path: 'templates/vscode-extension',
|
|
63
|
-
},
|
|
64
56
|
};
|
|
65
57
|
|
|
66
58
|
/**
|
|
@@ -115,9 +115,10 @@ Follow this proven TDD workflow:
|
|
|
115
115
|
7. **Archive**: Complete and archive finished work
|
|
116
116
|
|
|
117
117
|
Key commands:
|
|
118
|
-
- \`caws
|
|
119
|
-
- \`caws
|
|
120
|
-
- \`caws
|
|
118
|
+
- \`caws validate --spec-id FEAT-001\` - Validate current work
|
|
119
|
+
- \`caws status --spec-id FEAT-001\` - Check progress
|
|
120
|
+
- \`caws evaluate --spec-id FEAT-001\` - Quality/readiness scoring
|
|
121
|
+
- \`caws burnup --spec-id FEAT-001\` - Budget burn-up report
|
|
121
122
|
- \`caws archive <change-id>\` - Complete work
|
|
122
123
|
`,
|
|
123
124
|
action: 'Try: caws status --visual',
|
|
@@ -235,18 +236,18 @@ Let's get you started!
|
|
|
235
236
|
First, ensure CAWS is properly initialized:
|
|
236
237
|
|
|
237
238
|
1. Initialize CAWS: \`caws init .\`
|
|
238
|
-
2.
|
|
239
|
+
2. Create a feature spec: \`caws specs create FEAT-001 --type feature --title "My Feature"\`
|
|
239
240
|
3. Set up git hooks: \`caws hooks install\`
|
|
240
241
|
4. Initialize provenance: \`caws provenance init\`
|
|
241
242
|
|
|
242
243
|
For existing projects, use: \`caws scaffold\`
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
-
|
|
246
|
-
-
|
|
247
|
-
-
|
|
245
|
+
All commands support \`--spec-id\` to target a specific feature spec:
|
|
246
|
+
- \`caws validate --spec-id FEAT-001\`
|
|
247
|
+
- \`caws status --spec-id FEAT-001\`
|
|
248
|
+
- \`caws evaluate --spec-id FEAT-001\`
|
|
248
249
|
`,
|
|
249
|
-
action: 'Try: caws
|
|
250
|
+
action: 'Try: caws specs list',
|
|
250
251
|
verify: 'mode_setup',
|
|
251
252
|
},
|
|
252
253
|
{
|
|
@@ -14,7 +14,14 @@ const {
|
|
|
14
14
|
} = require('../validation/spec-validation');
|
|
15
15
|
|
|
16
16
|
// Import spec resolution system
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
resolveSpec,
|
|
19
|
+
suggestMigration,
|
|
20
|
+
loadSpecsRegistry,
|
|
21
|
+
} = require('../utils/spec-resolver');
|
|
22
|
+
const { recordValidation } = require('../utils/working-state');
|
|
23
|
+
const { appendEvent } = require('../utils/event-log');
|
|
24
|
+
const { lifecycle, EVENTS } = require('../utils/lifecycle-events');
|
|
18
25
|
|
|
19
26
|
/**
|
|
20
27
|
* Validate command handler
|
|
@@ -75,12 +82,14 @@ async function validateCommand(specFile, options = {}) {
|
|
|
75
82
|
// Check scope conflicts (if multiple specs exist)
|
|
76
83
|
const { checkMultiSpecStatus } = require('../utils/spec-resolver');
|
|
77
84
|
const multiSpecStatus = await checkMultiSpecStatus();
|
|
85
|
+
const registry =
|
|
86
|
+
multiSpecStatus.registry ||
|
|
87
|
+
await loadSpecsRegistry();
|
|
88
|
+
const specIds = Object.keys(registry.specs || {});
|
|
78
89
|
|
|
79
|
-
if (
|
|
90
|
+
if (specIds.length > 1) {
|
|
80
91
|
const { checkScopeConflicts } = require('../utils/spec-resolver');
|
|
81
|
-
const conflicts = await checkScopeConflicts(
|
|
82
|
-
Object.keys(multiSpecStatus.registry?.specs || {})
|
|
83
|
-
);
|
|
92
|
+
const conflicts = await checkScopeConflicts(specIds);
|
|
84
93
|
|
|
85
94
|
if (conflicts.length > 0) {
|
|
86
95
|
const myConflicts = conflicts.filter((c) => c.spec1 === spec.id || c.spec2 === spec.id);
|
|
@@ -135,6 +144,60 @@ async function validateCommand(specFile, options = {}) {
|
|
|
135
144
|
|
|
136
145
|
const finalResult = enhancedValidation;
|
|
137
146
|
|
|
147
|
+
// Record to working state (Phase 1 dual-write: state layer + event log)
|
|
148
|
+
const validationGrade = finalResult.complianceScore !== undefined
|
|
149
|
+
? getComplianceGrade(finalResult.complianceScore)
|
|
150
|
+
: null;
|
|
151
|
+
const validationPayload = {
|
|
152
|
+
passed: finalResult.valid,
|
|
153
|
+
compliance_score: finalResult.complianceScore ?? null,
|
|
154
|
+
grade: validationGrade,
|
|
155
|
+
error_count: (finalResult.errors || []).length,
|
|
156
|
+
warning_count: (finalResult.warnings || []).length,
|
|
157
|
+
};
|
|
158
|
+
// CAWSFIX-02: guard recordValidation with the same `spec && spec.id`
|
|
159
|
+
// check that gates.js already uses and that the appendEvent call below
|
|
160
|
+
// enforces. Without this, legacy working-specs without an id silently
|
|
161
|
+
// wrote `.caws/state/undefined.json` — now blocked by the state-layer
|
|
162
|
+
// fence, but guarding here keeps the intent explicit.
|
|
163
|
+
if (spec && spec.id) {
|
|
164
|
+
try {
|
|
165
|
+
recordValidation(spec.id, validationPayload);
|
|
166
|
+
} catch { /* non-fatal */ }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// EVLOG-001: emit event log entry alongside state write. Only if
|
|
170
|
+
// spec.id is present — the fence in appendEvent would throw otherwise,
|
|
171
|
+
// which is intentional for the undefined.json bug class but wrong for
|
|
172
|
+
// legitimate legacy specs without an id field. Errors here are NOT
|
|
173
|
+
// swallowed: a failure to append an event is a real defect we want to
|
|
174
|
+
// surface, not a silent data-loss event.
|
|
175
|
+
if (spec && spec.id) {
|
|
176
|
+
await appendEvent(
|
|
177
|
+
{ actor: 'cli', event: 'validation_completed', spec_id: spec.id, data: validationPayload }
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Emit lifecycle event
|
|
182
|
+
try {
|
|
183
|
+
const grade = finalResult.complianceScore !== undefined
|
|
184
|
+
? getComplianceGrade(finalResult.complianceScore)
|
|
185
|
+
: null;
|
|
186
|
+
if (finalResult.valid) {
|
|
187
|
+
lifecycle.emit(EVENTS.VALIDATION_PASSED, {
|
|
188
|
+
specId: spec.id, grade, complianceScore: finalResult.complianceScore,
|
|
189
|
+
timestamp: new Date().toISOString(),
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
lifecycle.emit(EVENTS.VALIDATION_FAILED, {
|
|
193
|
+
specId: spec.id, errors: finalResult.errors || [],
|
|
194
|
+
errorCount: (finalResult.errors || []).length,
|
|
195
|
+
warningCount: (finalResult.warnings || []).length,
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
} catch { /* non-fatal */ }
|
|
200
|
+
|
|
138
201
|
// Format output based on requested format
|
|
139
202
|
if (options.format === 'json') {
|
|
140
203
|
// Structured JSON output matching CAWSValidationResult
|
|
@@ -236,7 +299,8 @@ async function validateCommand(specFile, options = {}) {
|
|
|
236
299
|
if (error.message === 'Spec ID required when multiple specs exist' && !options.specId) {
|
|
237
300
|
const { checkMultiSpecStatus } = require('../utils/spec-resolver');
|
|
238
301
|
const status = await checkMultiSpecStatus();
|
|
239
|
-
const
|
|
302
|
+
const registry = status.registry || await loadSpecsRegistry();
|
|
303
|
+
const specIds = Object.keys(registry.specs || {});
|
|
240
304
|
|
|
241
305
|
if (specIds.length === 0) {
|
|
242
306
|
console.error(chalk.red('No specs found in registry'));
|
|
@@ -7,13 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs-extra');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const yaml = require('js-yaml');
|
|
11
10
|
const chalk = require('chalk');
|
|
12
11
|
const { execFileSync } = require('child_process');
|
|
13
12
|
const { findProjectRoot } = require('../utils/detection');
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
13
|
+
const { resolveSpec } = require('../utils/spec-resolver');
|
|
14
|
+
const { recordACVerification } = require('../utils/working-state');
|
|
15
|
+
const { appendEvent } = require('../utils/event-log');
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Detect the project's test runner from config files.
|
|
@@ -318,55 +317,6 @@ function verifySpec(spec, projectRoot, options = {}) {
|
|
|
318
317
|
};
|
|
319
318
|
}
|
|
320
319
|
|
|
321
|
-
/**
|
|
322
|
-
* Load all active specs from .caws/specs/
|
|
323
|
-
* @param {string} projectRoot
|
|
324
|
-
* @param {string} [targetSpecId] - If provided, only load this spec
|
|
325
|
-
* @returns {Array<{path: string, spec: Object}>}
|
|
326
|
-
*/
|
|
327
|
-
function loadSpecs(projectRoot, targetSpecId) {
|
|
328
|
-
const specs = [];
|
|
329
|
-
const specsDir = path.join(projectRoot, SPECS_DIR);
|
|
330
|
-
|
|
331
|
-
if (targetSpecId) {
|
|
332
|
-
const specPath = path.join(specsDir, `${targetSpecId}.yaml`);
|
|
333
|
-
if (!fs.existsSync(specPath)) {
|
|
334
|
-
const ymlPath = path.join(specsDir, `${targetSpecId}.yml`);
|
|
335
|
-
if (!fs.existsSync(ymlPath)) {
|
|
336
|
-
throw new Error(`Spec '${targetSpecId}' not found at ${specPath}`);
|
|
337
|
-
}
|
|
338
|
-
specs.push({ path: ymlPath, spec: yaml.load(fs.readFileSync(ymlPath, 'utf8')) });
|
|
339
|
-
} else {
|
|
340
|
-
specs.push({ path: specPath, spec: yaml.load(fs.readFileSync(specPath, 'utf8')) });
|
|
341
|
-
}
|
|
342
|
-
return specs;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Also check working-spec.yaml
|
|
346
|
-
const workingSpec = path.join(projectRoot, '.caws/working-spec.yaml');
|
|
347
|
-
if (fs.existsSync(workingSpec)) {
|
|
348
|
-
try {
|
|
349
|
-
const s = yaml.load(fs.readFileSync(workingSpec, 'utf8'));
|
|
350
|
-
if (s && !TERMINAL_STATUSES.has(s.status)) {
|
|
351
|
-
specs.push({ path: workingSpec, spec: s });
|
|
352
|
-
}
|
|
353
|
-
} catch (_) { /* ignore unreadable config */ }
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (fs.existsSync(specsDir)) {
|
|
357
|
-
for (const f of fs.readdirSync(specsDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))) {
|
|
358
|
-
try {
|
|
359
|
-
const s = yaml.load(fs.readFileSync(path.join(specsDir, f), 'utf8'));
|
|
360
|
-
if (s && !TERMINAL_STATUSES.has(s.status)) {
|
|
361
|
-
specs.push({ path: path.join(specsDir, f), spec: s });
|
|
362
|
-
}
|
|
363
|
-
} catch (_) { /* ignore unreadable config */ }
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return specs;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
320
|
/**
|
|
371
321
|
* Main command handler
|
|
372
322
|
*/
|
|
@@ -379,33 +329,51 @@ async function verifyAcsCommand(options = {}) {
|
|
|
379
329
|
process.exit(1);
|
|
380
330
|
}
|
|
381
331
|
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
332
|
+
const resolved = await resolveSpec({
|
|
333
|
+
specId: options.specId,
|
|
334
|
+
warnLegacy: false,
|
|
335
|
+
});
|
|
388
336
|
|
|
389
337
|
const allResults = [];
|
|
390
338
|
let totalAcs = 0, totalMechanical = 0, totalPass = 0, totalFail = 0, totalUnchecked = 0;
|
|
339
|
+
const result = verifySpec(resolved.spec, projectRoot, {
|
|
340
|
+
run: options.run || false,
|
|
341
|
+
runner: options.runner,
|
|
342
|
+
});
|
|
343
|
+
result.path = resolved.path;
|
|
344
|
+
result.type = resolved.type;
|
|
345
|
+
allResults.push(result);
|
|
346
|
+
|
|
347
|
+
for (const r of result.results) {
|
|
348
|
+
totalAcs++;
|
|
349
|
+
if (r.status === 'PASS') { totalPass++; totalMechanical++; }
|
|
350
|
+
else if (r.status === 'FAIL') { totalFail++; totalMechanical++; }
|
|
351
|
+
else { totalUnchecked++; }
|
|
352
|
+
}
|
|
391
353
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
354
|
+
// Record to working state (Phase 1 dual-write: state layer + event log)
|
|
355
|
+
const acPayload = {
|
|
356
|
+
total: totalAcs,
|
|
357
|
+
pass: totalPass,
|
|
358
|
+
fail: totalFail,
|
|
359
|
+
unchecked: totalUnchecked,
|
|
360
|
+
results: result.results,
|
|
361
|
+
};
|
|
362
|
+
// CAWSFIX-02: guard recordACVerification with `resolved.spec && resolved.spec.id`
|
|
363
|
+
// check to prevent the .caws/state/undefined.json bug class. Matches the
|
|
364
|
+
// pattern gates.js already uses and the appendEvent call below.
|
|
365
|
+
if (resolved.spec && resolved.spec.id) {
|
|
366
|
+
try {
|
|
367
|
+
recordACVerification(resolved.spec.id, acPayload, projectRoot);
|
|
368
|
+
} catch { /* non-fatal */ }
|
|
369
|
+
}
|
|
401
370
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
371
|
+
// EVLOG-001: emit verify_acs_completed event alongside state write.
|
|
372
|
+
if (resolved.spec && resolved.spec.id) {
|
|
373
|
+
await appendEvent(
|
|
374
|
+
{ actor: 'cli', event: 'verify_acs_completed', spec_id: resolved.spec.id, data: acPayload },
|
|
375
|
+
{ projectRoot }
|
|
376
|
+
);
|
|
409
377
|
}
|
|
410
378
|
|
|
411
379
|
// Output
|
|
@@ -421,7 +389,11 @@ async function verifyAcsCommand(options = {}) {
|
|
|
421
389
|
// Table output
|
|
422
390
|
for (const result of allResults) {
|
|
423
391
|
console.log(chalk.cyan(`\n## ${result.specId} — ${result.title}`));
|
|
424
|
-
console.log(
|
|
392
|
+
console.log(
|
|
393
|
+
chalk.gray(
|
|
394
|
+
` Test runner: ${result.runner} | Mode: ${options.run ? 'run' : 'collect-only'} | Spec: ${result.type} -> ${result.path}`
|
|
395
|
+
)
|
|
396
|
+
);
|
|
425
397
|
console.log();
|
|
426
398
|
|
|
427
399
|
const widths = { id: 8, desc: 40, method: 14, status: 10 };
|