@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.
Files changed (286) hide show
  1. package/README.md +71 -32
  2. package/dist/budget-derivation.js +221 -74
  3. package/dist/commands/archive.js +67 -28
  4. package/dist/commands/burnup.js +20 -11
  5. package/dist/commands/diagnose.js +34 -22
  6. package/dist/commands/evaluate.js +41 -15
  7. package/dist/commands/gates.js +149 -0
  8. package/dist/commands/init.js +150 -19
  9. package/dist/commands/iterate.js +81 -4
  10. package/dist/commands/parallel.js +4 -0
  11. package/dist/commands/plan.js +9 -19
  12. package/dist/commands/provenance.js +53 -17
  13. package/dist/commands/quality-monitor.js +64 -45
  14. package/dist/commands/scope.js +264 -0
  15. package/dist/commands/sidecar.js +74 -0
  16. package/dist/commands/specs.js +381 -45
  17. package/dist/commands/status.js +117 -9
  18. package/dist/commands/templates.js +0 -8
  19. package/dist/commands/tutorial.js +10 -9
  20. package/dist/commands/validate.js +70 -6
  21. package/dist/commands/verify-acs.js +48 -76
  22. package/dist/commands/waivers.js +212 -13
  23. package/dist/commands/worktree.js +131 -26
  24. package/dist/error-handler.js +2 -13
  25. package/dist/gates/budget-limit.js +121 -0
  26. package/dist/gates/feedback.js +260 -0
  27. package/dist/gates/format.js +179 -0
  28. package/dist/gates/god-object.js +117 -0
  29. package/dist/gates/pipeline.js +167 -0
  30. package/dist/gates/scope-boundary.js +93 -0
  31. package/dist/gates/spec-completeness.js +109 -0
  32. package/dist/gates/todo-detection.js +205 -0
  33. package/dist/index.js +157 -151
  34. package/dist/parallel/parallel-manager.js +3 -3
  35. package/dist/policy/PolicyManager.js +51 -17
  36. package/dist/scaffold/claude-hooks.js +24 -1
  37. package/dist/scaffold/git-hooks.js +45 -102
  38. package/dist/scaffold/index.js +4 -3
  39. package/dist/session/session-manager.js +105 -14
  40. package/dist/sidecars/index.js +33 -0
  41. package/dist/sidecars/listeners.js +40 -0
  42. package/dist/sidecars/provenance-summary.js +238 -0
  43. package/dist/sidecars/quality-gaps.js +258 -0
  44. package/dist/sidecars/schema.js +149 -0
  45. package/dist/sidecars/spec-drift.js +151 -0
  46. package/dist/sidecars/waiver-draft.js +176 -0
  47. package/dist/templates/.caws/schemas/policy.schema.json +112 -0
  48. package/dist/templates/.caws/schemas/scope.schema.json +3 -3
  49. package/dist/templates/.caws/schemas/waivers.schema.json +96 -20
  50. package/dist/templates/.caws/schemas/working-spec.schema.json +264 -57
  51. package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
  52. package/dist/templates/.caws/templates/working-spec.template.yml +10 -4
  53. package/dist/templates/.caws/tools/scope-guard.js +66 -15
  54. package/dist/templates/.claude/README.md +1 -1
  55. package/dist/templates/.claude/hooks/audit.sh +0 -0
  56. package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
  57. package/dist/templates/.claude/hooks/classify_command.py +592 -0
  58. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  59. package/dist/templates/.claude/hooks/protected-paths.sh +39 -0
  60. package/dist/templates/.claude/hooks/quality-check.sh +23 -10
  61. package/dist/templates/.claude/hooks/scope-guard.sh +136 -55
  62. package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
  63. package/dist/templates/.claude/hooks/session-log.sh +76 -3
  64. package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  65. package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
  66. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  67. package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
  68. package/dist/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  69. package/dist/templates/.claude/settings.json +31 -0
  70. package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  71. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  72. package/dist/templates/.cursor/hooks/session-log.sh +924 -0
  73. package/dist/templates/.cursor/hooks.json +25 -0
  74. package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  75. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  76. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  77. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  78. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  79. package/dist/templates/.github/copilot-instructions.md +5 -5
  80. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  81. package/dist/templates/.junie/guidelines.md +2 -2
  82. package/dist/templates/.vscode/settings.json +3 -1
  83. package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  84. package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  85. package/dist/templates/CLAUDE.md +77 -8
  86. package/dist/templates/agents.md +50 -9
  87. package/dist/templates/docs/README.md +8 -7
  88. package/dist/templates/scripts/new_feature.sh +80 -0
  89. package/dist/test-analysis.js +43 -30
  90. package/dist/tool-loader.js +1 -1
  91. package/dist/utils/agent-session.js +202 -0
  92. package/dist/utils/detection.js +8 -2
  93. package/dist/utils/event-log.js +584 -0
  94. package/dist/utils/event-renderer.js +521 -0
  95. package/dist/utils/finalization.js +7 -6
  96. package/dist/utils/gitignore-updater.js +3 -0
  97. package/dist/utils/lifecycle-events.js +94 -0
  98. package/dist/utils/quality-gates-utils.js +29 -44
  99. package/dist/utils/schema-validator.js +50 -0
  100. package/dist/utils/spec-resolver.js +93 -21
  101. package/dist/utils/working-state.js +530 -0
  102. package/dist/validation/spec-validation.js +191 -31
  103. package/dist/waivers-manager.js +144 -6
  104. package/dist/worktree/worktree-manager.js +598 -95
  105. package/package.json +9 -8
  106. package/templates/.caws/schemas/policy.schema.json +112 -0
  107. package/templates/.caws/schemas/scope.schema.json +3 -3
  108. package/templates/.caws/schemas/waivers.schema.json +96 -20
  109. package/templates/.caws/schemas/working-spec.schema.json +264 -57
  110. package/templates/.caws/schemas/worktrees.schema.json +3 -1
  111. package/templates/.caws/templates/working-spec.template.yml +10 -4
  112. package/templates/.caws/tools/scope-guard.js +66 -15
  113. package/templates/.claude/README.md +1 -1
  114. package/templates/.claude/hooks/block-dangerous.sh +52 -11
  115. package/templates/.claude/hooks/classify_command.py +592 -0
  116. package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  117. package/templates/.claude/hooks/protected-paths.sh +39 -0
  118. package/templates/.claude/hooks/quality-check.sh +23 -10
  119. package/templates/.claude/hooks/scope-guard.sh +136 -55
  120. package/templates/.claude/hooks/session-caws-status.sh +2 -2
  121. package/templates/.claude/hooks/session-log.sh +76 -3
  122. package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  123. package/templates/.claude/hooks/test_classify_command.py +370 -0
  124. package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  125. package/templates/.claude/hooks/worktree-guard.sh +2 -2
  126. package/templates/.claude/hooks/worktree-write-guard.sh +97 -4
  127. package/templates/.claude/settings.json +31 -0
  128. package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  129. package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  130. package/templates/.cursor/hooks/session-log.sh +924 -0
  131. package/templates/.cursor/hooks.json +25 -0
  132. package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  133. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  134. package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  135. package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  136. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  137. package/templates/.github/copilot-instructions.md +5 -5
  138. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  139. package/templates/.junie/guidelines.md +2 -2
  140. package/templates/.vscode/settings.json +3 -1
  141. package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  142. package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  143. package/templates/CLAUDE.md +77 -8
  144. package/templates/{AGENTS.md → agents.md} +50 -9
  145. package/templates/docs/README.md +8 -7
  146. package/templates/scripts/new_feature.sh +80 -0
  147. package/dist/budget-derivation.d.ts +0 -74
  148. package/dist/budget-derivation.d.ts.map +0 -1
  149. package/dist/cicd-optimizer.d.ts +0 -142
  150. package/dist/cicd-optimizer.d.ts.map +0 -1
  151. package/dist/commands/archive.d.ts +0 -51
  152. package/dist/commands/archive.d.ts.map +0 -1
  153. package/dist/commands/burnup.d.ts +0 -6
  154. package/dist/commands/burnup.d.ts.map +0 -1
  155. package/dist/commands/diagnose.d.ts +0 -52
  156. package/dist/commands/diagnose.d.ts.map +0 -1
  157. package/dist/commands/evaluate.d.ts +0 -8
  158. package/dist/commands/evaluate.d.ts.map +0 -1
  159. package/dist/commands/init.d.ts +0 -5
  160. package/dist/commands/init.d.ts.map +0 -1
  161. package/dist/commands/iterate.d.ts +0 -8
  162. package/dist/commands/iterate.d.ts.map +0 -1
  163. package/dist/commands/mode.d.ts +0 -25
  164. package/dist/commands/mode.d.ts.map +0 -1
  165. package/dist/commands/parallel.d.ts +0 -7
  166. package/dist/commands/parallel.d.ts.map +0 -1
  167. package/dist/commands/plan.d.ts +0 -49
  168. package/dist/commands/plan.d.ts.map +0 -1
  169. package/dist/commands/provenance.d.ts +0 -32
  170. package/dist/commands/provenance.d.ts.map +0 -1
  171. package/dist/commands/quality-gates.d.ts +0 -6
  172. package/dist/commands/quality-gates.d.ts.map +0 -1
  173. package/dist/commands/quality-gates.js +0 -444
  174. package/dist/commands/quality-monitor.d.ts +0 -17
  175. package/dist/commands/quality-monitor.d.ts.map +0 -1
  176. package/dist/commands/session.d.ts +0 -7
  177. package/dist/commands/session.d.ts.map +0 -1
  178. package/dist/commands/specs.d.ts +0 -77
  179. package/dist/commands/specs.d.ts.map +0 -1
  180. package/dist/commands/status.d.ts +0 -44
  181. package/dist/commands/status.d.ts.map +0 -1
  182. package/dist/commands/templates.d.ts +0 -74
  183. package/dist/commands/templates.d.ts.map +0 -1
  184. package/dist/commands/tool.d.ts +0 -13
  185. package/dist/commands/tool.d.ts.map +0 -1
  186. package/dist/commands/troubleshoot.d.ts +0 -8
  187. package/dist/commands/troubleshoot.d.ts.map +0 -1
  188. package/dist/commands/troubleshoot.js +0 -104
  189. package/dist/commands/tutorial.d.ts +0 -55
  190. package/dist/commands/tutorial.d.ts.map +0 -1
  191. package/dist/commands/validate.d.ts +0 -15
  192. package/dist/commands/validate.d.ts.map +0 -1
  193. package/dist/commands/waivers.d.ts +0 -8
  194. package/dist/commands/waivers.d.ts.map +0 -1
  195. package/dist/commands/workflow.d.ts +0 -85
  196. package/dist/commands/workflow.d.ts.map +0 -1
  197. package/dist/commands/worktree.d.ts +0 -7
  198. package/dist/commands/worktree.d.ts.map +0 -1
  199. package/dist/config/index.d.ts +0 -29
  200. package/dist/config/index.d.ts.map +0 -1
  201. package/dist/config/lite-scope.d.ts +0 -33
  202. package/dist/config/lite-scope.d.ts.map +0 -1
  203. package/dist/config/modes.d.ts +0 -264
  204. package/dist/config/modes.d.ts.map +0 -1
  205. package/dist/constants/spec-types.d.ts +0 -93
  206. package/dist/constants/spec-types.d.ts.map +0 -1
  207. package/dist/error-handler.d.ts +0 -151
  208. package/dist/error-handler.d.ts.map +0 -1
  209. package/dist/generators/jest-config-generator.d.ts +0 -32
  210. package/dist/generators/jest-config-generator.d.ts.map +0 -1
  211. package/dist/generators/jest-config.d.ts +0 -32
  212. package/dist/generators/jest-config.d.ts.map +0 -1
  213. package/dist/generators/jest-config.js +0 -242
  214. package/dist/generators/working-spec.d.ts +0 -13
  215. package/dist/generators/working-spec.d.ts.map +0 -1
  216. package/dist/index-new.d.ts +0 -5
  217. package/dist/index-new.d.ts.map +0 -1
  218. package/dist/index-new.js +0 -317
  219. package/dist/index.d.ts +0 -5
  220. package/dist/index.d.ts.map +0 -1
  221. package/dist/index.js.backup +0 -4711
  222. package/dist/minimal-cli.d.ts +0 -3
  223. package/dist/minimal-cli.d.ts.map +0 -1
  224. package/dist/parallel/parallel-manager.d.ts +0 -67
  225. package/dist/parallel/parallel-manager.d.ts.map +0 -1
  226. package/dist/policy/PolicyManager.d.ts +0 -104
  227. package/dist/policy/PolicyManager.d.ts.map +0 -1
  228. package/dist/scaffold/claude-hooks.d.ts +0 -28
  229. package/dist/scaffold/claude-hooks.d.ts.map +0 -1
  230. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  231. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  232. package/dist/scaffold/git-hooks.d.ts +0 -38
  233. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  234. package/dist/scaffold/index.d.ts +0 -17
  235. package/dist/scaffold/index.d.ts.map +0 -1
  236. package/dist/session/session-manager.d.ts +0 -94
  237. package/dist/session/session-manager.d.ts.map +0 -1
  238. package/dist/spec/SpecFileManager.d.ts +0 -146
  239. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  240. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
  241. package/dist/templates/.github/copilot/instructions.md +0 -311
  242. package/dist/test-analysis.d.ts +0 -231
  243. package/dist/test-analysis.d.ts.map +0 -1
  244. package/dist/tool-interface.d.ts +0 -236
  245. package/dist/tool-interface.d.ts.map +0 -1
  246. package/dist/tool-loader.d.ts +0 -77
  247. package/dist/tool-loader.d.ts.map +0 -1
  248. package/dist/tool-validator.d.ts +0 -72
  249. package/dist/tool-validator.d.ts.map +0 -1
  250. package/dist/utils/async-utils.d.ts +0 -73
  251. package/dist/utils/async-utils.d.ts.map +0 -1
  252. package/dist/utils/command-wrapper.d.ts +0 -66
  253. package/dist/utils/command-wrapper.d.ts.map +0 -1
  254. package/dist/utils/detection.d.ts +0 -14
  255. package/dist/utils/detection.d.ts.map +0 -1
  256. package/dist/utils/error-categories.d.ts +0 -52
  257. package/dist/utils/error-categories.d.ts.map +0 -1
  258. package/dist/utils/finalization.d.ts +0 -17
  259. package/dist/utils/finalization.d.ts.map +0 -1
  260. package/dist/utils/git-lock.d.ts +0 -13
  261. package/dist/utils/git-lock.d.ts.map +0 -1
  262. package/dist/utils/gitignore-updater.d.ts +0 -39
  263. package/dist/utils/gitignore-updater.d.ts.map +0 -1
  264. package/dist/utils/ide-detection.d.ts +0 -89
  265. package/dist/utils/ide-detection.d.ts.map +0 -1
  266. package/dist/utils/project-analysis.d.ts +0 -34
  267. package/dist/utils/project-analysis.d.ts.map +0 -1
  268. package/dist/utils/promise-utils.d.ts +0 -30
  269. package/dist/utils/promise-utils.d.ts.map +0 -1
  270. package/dist/utils/quality-gates-utils.d.ts +0 -49
  271. package/dist/utils/quality-gates-utils.d.ts.map +0 -1
  272. package/dist/utils/quality-gates.d.ts +0 -49
  273. package/dist/utils/quality-gates.d.ts.map +0 -1
  274. package/dist/utils/quality-gates.js +0 -402
  275. package/dist/utils/spec-resolver.d.ts +0 -80
  276. package/dist/utils/spec-resolver.d.ts.map +0 -1
  277. package/dist/utils/typescript-detector.d.ts +0 -66
  278. package/dist/utils/typescript-detector.d.ts.map +0 -1
  279. package/dist/utils/yaml-validation.d.ts +0 -32
  280. package/dist/utils/yaml-validation.d.ts.map +0 -1
  281. package/dist/validation/spec-validation.d.ts +0 -43
  282. package/dist/validation/spec-validation.d.ts.map +0 -1
  283. package/dist/waivers-manager.d.ts +0 -167
  284. package/dist/waivers-manager.d.ts.map +0 -1
  285. package/dist/worktree/worktree-manager.d.ts +0 -54
  286. package/dist/worktree/worktree-manager.d.ts.map +0 -1
@@ -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 [spec, specs, hooks, provenance, waivers, gates] = await parallel([
898
- () => loadWorkingSpec(options.spec || '.caws/working-spec.yaml'),
899
- () => loadSpecsFromMultiSpec(),
900
- () => checkGitHooks(),
901
- () => loadProvenanceChain(),
902
- () => loadWaiverStatus(),
903
- () => checkQualityGates(),
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
- true
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 progress update --criterion-id A1 --status completed\`
119
- - \`caws validate\` - Validate current work
120
- - \`caws status --visual\` - Check progress
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. Choose your complexity mode: \`caws mode set --interactive\`
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
- Choose a mode that fits your project:
245
- - Simple: Small projects, quick prototyping
246
- - Standard: Most teams and projects
247
- - Enterprise: Large teams, compliance requirements
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 mode current',
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 { resolveSpec, suggestMigration } = require('../utils/spec-resolver');
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 (multiSpecStatus.specCount > 1) {
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 specIds = Object.keys(status.registry?.specs || {});
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 SPECS_DIR = '.caws/specs';
16
- const TERMINAL_STATUSES = new Set(['completed', 'closed', 'archived']);
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 specs = loadSpecs(projectRoot, options.specId);
383
-
384
- if (specs.length === 0) {
385
- console.log(chalk.yellow('No active specs found.'));
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
- for (const { spec } of specs) {
393
- const acs = extractACs(spec);
394
- if (acs.length === 0) continue;
395
-
396
- const result = verifySpec(spec, projectRoot, {
397
- run: options.run || false,
398
- runner: options.runner,
399
- });
400
- allResults.push(result);
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
- // Tally
403
- for (const r of result.results) {
404
- totalAcs++;
405
- if (r.status === 'PASS') { totalPass++; totalMechanical++; }
406
- else if (r.status === 'FAIL') { totalFail++; totalMechanical++; }
407
- else { totalUnchecked++; }
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(chalk.gray(` Test runner: ${result.runner} | Mode: ${options.run ? 'run' : 'collect-only'}`));
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 };