@paths.design/caws-cli 9.3.2 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +58 -27
  2. package/dist/commands/archive.js +67 -28
  3. package/dist/commands/burnup.js +20 -11
  4. package/dist/commands/diagnose.js +34 -22
  5. package/dist/commands/evaluate.js +27 -15
  6. package/dist/commands/gates.js +122 -0
  7. package/dist/commands/init.js +143 -15
  8. package/dist/commands/iterate.js +77 -4
  9. package/dist/commands/parallel.js +4 -0
  10. package/dist/commands/plan.js +9 -19
  11. package/dist/commands/provenance.js +53 -17
  12. package/dist/commands/quality-monitor.js +64 -45
  13. package/dist/commands/sidecar.js +71 -0
  14. package/dist/commands/specs.js +233 -44
  15. package/dist/commands/status.js +113 -9
  16. package/dist/commands/tutorial.js +10 -9
  17. package/dist/commands/validate.js +49 -6
  18. package/dist/commands/verify-acs.js +35 -78
  19. package/dist/commands/waivers.js +69 -12
  20. package/dist/commands/worktree.js +50 -25
  21. package/dist/error-handler.js +2 -13
  22. package/dist/gates/budget-limit.js +116 -0
  23. package/dist/gates/feedback.js +260 -0
  24. package/dist/gates/format.js +179 -0
  25. package/dist/gates/god-object.js +117 -0
  26. package/dist/gates/pipeline.js +167 -0
  27. package/dist/gates/scope-boundary.js +93 -0
  28. package/dist/gates/spec-completeness.js +102 -0
  29. package/dist/gates/todo-detection.js +205 -0
  30. package/dist/index.js +130 -151
  31. package/dist/parallel/parallel-manager.js +3 -3
  32. package/dist/policy/PolicyManager.js +42 -10
  33. package/dist/scaffold/claude-hooks.js +24 -1
  34. package/dist/scaffold/git-hooks.js +45 -102
  35. package/dist/scaffold/index.js +4 -3
  36. package/dist/session/session-manager.js +71 -14
  37. package/dist/sidecars/index.js +33 -0
  38. package/dist/sidecars/listeners.js +40 -0
  39. package/dist/sidecars/provenance-summary.js +238 -0
  40. package/dist/sidecars/quality-gaps.js +258 -0
  41. package/dist/sidecars/schema.js +149 -0
  42. package/dist/sidecars/spec-drift.js +151 -0
  43. package/dist/sidecars/waiver-draft.js +176 -0
  44. package/dist/templates/.caws/schemas/policy.schema.json +50 -0
  45. package/dist/templates/.caws/schemas/waivers.schema.json +30 -24
  46. package/dist/templates/.caws/schemas/working-spec.schema.json +51 -8
  47. package/dist/templates/.caws/schemas/worktrees.schema.json +3 -1
  48. package/dist/templates/.caws/templates/working-spec.template.yml +7 -3
  49. package/dist/templates/.claude/hooks/audit.sh +0 -0
  50. package/dist/templates/.claude/hooks/block-dangerous.sh +52 -11
  51. package/dist/templates/.claude/hooks/classify_command.py +592 -0
  52. package/dist/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  53. package/dist/templates/.claude/hooks/quality-check.sh +23 -10
  54. package/dist/templates/.claude/hooks/scope-guard.sh +34 -32
  55. package/dist/templates/.claude/hooks/session-caws-status.sh +2 -2
  56. package/dist/templates/.claude/hooks/session-log.sh +76 -3
  57. package/dist/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  58. package/dist/templates/.claude/hooks/test_classify_command.py +370 -0
  59. package/dist/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  60. package/dist/templates/.claude/hooks/worktree-guard.sh +2 -2
  61. package/dist/templates/.claude/hooks/worktree-write-guard.sh +1 -1
  62. package/dist/templates/.claude/settings.json +26 -0
  63. package/dist/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  64. package/dist/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  65. package/dist/templates/.cursor/hooks/session-log.sh +924 -0
  66. package/dist/templates/.cursor/hooks.json +25 -0
  67. package/dist/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  68. package/dist/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  69. package/dist/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  70. package/dist/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  71. package/dist/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  72. package/dist/templates/.github/copilot-instructions.md +5 -5
  73. package/dist/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  74. package/dist/templates/.junie/guidelines.md +2 -2
  75. package/dist/templates/.vscode/settings.json +3 -1
  76. package/dist/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  77. package/dist/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  78. package/dist/templates/CLAUDE.md +43 -8
  79. package/dist/templates/agents.md +29 -9
  80. package/dist/templates/docs/README.md +8 -7
  81. package/dist/templates/scripts/new_feature.sh +80 -0
  82. package/dist/test-analysis.js +43 -30
  83. package/dist/tool-loader.js +1 -1
  84. package/dist/utils/agent-session.js +202 -0
  85. package/dist/utils/detection.js +8 -2
  86. package/dist/utils/finalization.js +7 -6
  87. package/dist/utils/gitignore-updater.js +3 -0
  88. package/dist/utils/lifecycle-events.js +94 -0
  89. package/dist/utils/quality-gates-utils.js +29 -44
  90. package/dist/utils/schema-validator.js +42 -0
  91. package/dist/utils/spec-resolver.js +93 -21
  92. package/dist/utils/working-state.js +505 -0
  93. package/dist/validation/spec-validation.js +92 -22
  94. package/dist/waivers-manager.js +60 -6
  95. package/dist/worktree/worktree-manager.js +390 -93
  96. package/package.json +6 -6
  97. package/templates/.caws/schemas/policy.schema.json +50 -0
  98. package/templates/.caws/schemas/waivers.schema.json +30 -24
  99. package/templates/.caws/schemas/working-spec.schema.json +51 -8
  100. package/templates/.caws/schemas/worktrees.schema.json +3 -1
  101. package/templates/.caws/templates/working-spec.template.yml +7 -3
  102. package/templates/.claude/hooks/block-dangerous.sh +52 -11
  103. package/templates/.claude/hooks/classify_command.py +592 -0
  104. package/templates/.claude/hooks/doc-frontmatter-check.sh +173 -0
  105. package/templates/.claude/hooks/quality-check.sh +23 -10
  106. package/templates/.claude/hooks/scope-guard.sh +34 -32
  107. package/templates/.claude/hooks/session-caws-status.sh +2 -2
  108. package/templates/.claude/hooks/session-log.sh +76 -3
  109. package/templates/.claude/hooks/stop-worktree-check.sh +1 -1
  110. package/templates/.claude/hooks/test_classify_command.py +370 -0
  111. package/templates/.claude/hooks/test_wrapper_smoke.sh +96 -0
  112. package/templates/.claude/hooks/worktree-guard.sh +2 -2
  113. package/templates/.claude/hooks/worktree-write-guard.sh +1 -1
  114. package/templates/.claude/settings.json +26 -0
  115. package/templates/.cursor/hooks/caws-quality-check.sh +4 -4
  116. package/templates/.cursor/hooks/caws-scope-guard.sh +1 -1
  117. package/templates/.cursor/hooks/session-log.sh +924 -0
  118. package/templates/.cursor/hooks.json +25 -0
  119. package/templates/.cursor/rules/02-quality-gates.mdc +3 -5
  120. package/templates/.cursor/rules/10-documentation-quality-standards.mdc +6 -11
  121. package/templates/.cursor/rules/11-scope-management-waivers.mdc +14 -18
  122. package/templates/.cursor/rules/12-implementation-completeness.mdc +4 -4
  123. package/templates/.cursor/rules/13-language-agnostic-standards.mdc +3 -13
  124. package/templates/.github/copilot-instructions.md +5 -5
  125. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +1 -1
  126. package/templates/.junie/guidelines.md +2 -2
  127. package/templates/.vscode/settings.json +3 -1
  128. package/templates/.windsurf/rules/caws-quality-standards.md +2 -2
  129. package/templates/.windsurf/workflows/caws-guided-development.md +3 -3
  130. package/templates/CLAUDE.md +43 -8
  131. package/templates/{AGENTS.md → agents.md} +29 -9
  132. package/templates/docs/README.md +8 -7
  133. package/templates/scripts/new_feature.sh +80 -0
  134. package/dist/budget-derivation.d.ts +0 -74
  135. package/dist/budget-derivation.d.ts.map +0 -1
  136. package/dist/cicd-optimizer.d.ts +0 -142
  137. package/dist/cicd-optimizer.d.ts.map +0 -1
  138. package/dist/commands/archive.d.ts +0 -51
  139. package/dist/commands/archive.d.ts.map +0 -1
  140. package/dist/commands/burnup.d.ts +0 -6
  141. package/dist/commands/burnup.d.ts.map +0 -1
  142. package/dist/commands/diagnose.d.ts +0 -52
  143. package/dist/commands/diagnose.d.ts.map +0 -1
  144. package/dist/commands/evaluate.d.ts +0 -8
  145. package/dist/commands/evaluate.d.ts.map +0 -1
  146. package/dist/commands/init.d.ts +0 -5
  147. package/dist/commands/init.d.ts.map +0 -1
  148. package/dist/commands/iterate.d.ts +0 -8
  149. package/dist/commands/iterate.d.ts.map +0 -1
  150. package/dist/commands/mode.d.ts +0 -25
  151. package/dist/commands/mode.d.ts.map +0 -1
  152. package/dist/commands/parallel.d.ts +0 -7
  153. package/dist/commands/parallel.d.ts.map +0 -1
  154. package/dist/commands/plan.d.ts +0 -49
  155. package/dist/commands/plan.d.ts.map +0 -1
  156. package/dist/commands/provenance.d.ts +0 -32
  157. package/dist/commands/provenance.d.ts.map +0 -1
  158. package/dist/commands/quality-gates.d.ts +0 -6
  159. package/dist/commands/quality-gates.d.ts.map +0 -1
  160. package/dist/commands/quality-gates.js +0 -444
  161. package/dist/commands/quality-monitor.d.ts +0 -17
  162. package/dist/commands/quality-monitor.d.ts.map +0 -1
  163. package/dist/commands/session.d.ts +0 -7
  164. package/dist/commands/session.d.ts.map +0 -1
  165. package/dist/commands/specs.d.ts +0 -77
  166. package/dist/commands/specs.d.ts.map +0 -1
  167. package/dist/commands/status.d.ts +0 -44
  168. package/dist/commands/status.d.ts.map +0 -1
  169. package/dist/commands/templates.d.ts +0 -74
  170. package/dist/commands/templates.d.ts.map +0 -1
  171. package/dist/commands/tool.d.ts +0 -13
  172. package/dist/commands/tool.d.ts.map +0 -1
  173. package/dist/commands/troubleshoot.d.ts +0 -8
  174. package/dist/commands/troubleshoot.d.ts.map +0 -1
  175. package/dist/commands/troubleshoot.js +0 -104
  176. package/dist/commands/tutorial.d.ts +0 -55
  177. package/dist/commands/tutorial.d.ts.map +0 -1
  178. package/dist/commands/validate.d.ts +0 -15
  179. package/dist/commands/validate.d.ts.map +0 -1
  180. package/dist/commands/waivers.d.ts +0 -8
  181. package/dist/commands/waivers.d.ts.map +0 -1
  182. package/dist/commands/workflow.d.ts +0 -85
  183. package/dist/commands/workflow.d.ts.map +0 -1
  184. package/dist/commands/worktree.d.ts +0 -7
  185. package/dist/commands/worktree.d.ts.map +0 -1
  186. package/dist/config/index.d.ts +0 -29
  187. package/dist/config/index.d.ts.map +0 -1
  188. package/dist/config/lite-scope.d.ts +0 -33
  189. package/dist/config/lite-scope.d.ts.map +0 -1
  190. package/dist/config/modes.d.ts +0 -264
  191. package/dist/config/modes.d.ts.map +0 -1
  192. package/dist/constants/spec-types.d.ts +0 -93
  193. package/dist/constants/spec-types.d.ts.map +0 -1
  194. package/dist/error-handler.d.ts +0 -151
  195. package/dist/error-handler.d.ts.map +0 -1
  196. package/dist/generators/jest-config-generator.d.ts +0 -32
  197. package/dist/generators/jest-config-generator.d.ts.map +0 -1
  198. package/dist/generators/jest-config.d.ts +0 -32
  199. package/dist/generators/jest-config.d.ts.map +0 -1
  200. package/dist/generators/jest-config.js +0 -242
  201. package/dist/generators/working-spec.d.ts +0 -13
  202. package/dist/generators/working-spec.d.ts.map +0 -1
  203. package/dist/index-new.d.ts +0 -5
  204. package/dist/index-new.d.ts.map +0 -1
  205. package/dist/index-new.js +0 -317
  206. package/dist/index.d.ts +0 -5
  207. package/dist/index.d.ts.map +0 -1
  208. package/dist/index.js.backup +0 -4711
  209. package/dist/minimal-cli.d.ts +0 -3
  210. package/dist/minimal-cli.d.ts.map +0 -1
  211. package/dist/parallel/parallel-manager.d.ts +0 -67
  212. package/dist/parallel/parallel-manager.d.ts.map +0 -1
  213. package/dist/policy/PolicyManager.d.ts +0 -104
  214. package/dist/policy/PolicyManager.d.ts.map +0 -1
  215. package/dist/scaffold/claude-hooks.d.ts +0 -28
  216. package/dist/scaffold/claude-hooks.d.ts.map +0 -1
  217. package/dist/scaffold/cursor-hooks.d.ts +0 -7
  218. package/dist/scaffold/cursor-hooks.d.ts.map +0 -1
  219. package/dist/scaffold/git-hooks.d.ts +0 -38
  220. package/dist/scaffold/git-hooks.d.ts.map +0 -1
  221. package/dist/scaffold/index.d.ts +0 -17
  222. package/dist/scaffold/index.d.ts.map +0 -1
  223. package/dist/session/session-manager.d.ts +0 -94
  224. package/dist/session/session-manager.d.ts.map +0 -1
  225. package/dist/spec/SpecFileManager.d.ts +0 -146
  226. package/dist/spec/SpecFileManager.d.ts.map +0 -1
  227. package/dist/templates/.cursor/hooks/caws-tool-validation.sh +0 -121
  228. package/dist/templates/.github/copilot/instructions.md +0 -311
  229. package/dist/test-analysis.d.ts +0 -231
  230. package/dist/test-analysis.d.ts.map +0 -1
  231. package/dist/tool-interface.d.ts +0 -236
  232. package/dist/tool-interface.d.ts.map +0 -1
  233. package/dist/tool-loader.d.ts +0 -77
  234. package/dist/tool-loader.d.ts.map +0 -1
  235. package/dist/tool-validator.d.ts +0 -72
  236. package/dist/tool-validator.d.ts.map +0 -1
  237. package/dist/utils/async-utils.d.ts +0 -73
  238. package/dist/utils/async-utils.d.ts.map +0 -1
  239. package/dist/utils/command-wrapper.d.ts +0 -66
  240. package/dist/utils/command-wrapper.d.ts.map +0 -1
  241. package/dist/utils/detection.d.ts +0 -14
  242. package/dist/utils/detection.d.ts.map +0 -1
  243. package/dist/utils/error-categories.d.ts +0 -52
  244. package/dist/utils/error-categories.d.ts.map +0 -1
  245. package/dist/utils/finalization.d.ts +0 -17
  246. package/dist/utils/finalization.d.ts.map +0 -1
  247. package/dist/utils/git-lock.d.ts +0 -13
  248. package/dist/utils/git-lock.d.ts.map +0 -1
  249. package/dist/utils/gitignore-updater.d.ts +0 -39
  250. package/dist/utils/gitignore-updater.d.ts.map +0 -1
  251. package/dist/utils/ide-detection.d.ts +0 -89
  252. package/dist/utils/ide-detection.d.ts.map +0 -1
  253. package/dist/utils/project-analysis.d.ts +0 -34
  254. package/dist/utils/project-analysis.d.ts.map +0 -1
  255. package/dist/utils/promise-utils.d.ts +0 -30
  256. package/dist/utils/promise-utils.d.ts.map +0 -1
  257. package/dist/utils/quality-gates-utils.d.ts +0 -49
  258. package/dist/utils/quality-gates-utils.d.ts.map +0 -1
  259. package/dist/utils/quality-gates.d.ts +0 -49
  260. package/dist/utils/quality-gates.d.ts.map +0 -1
  261. package/dist/utils/quality-gates.js +0 -402
  262. package/dist/utils/spec-resolver.d.ts +0 -80
  263. package/dist/utils/spec-resolver.d.ts.map +0 -1
  264. package/dist/utils/typescript-detector.d.ts +0 -66
  265. package/dist/utils/typescript-detector.d.ts.map +0 -1
  266. package/dist/utils/yaml-validation.d.ts +0 -32
  267. package/dist/utils/yaml-validation.d.ts.map +0 -1
  268. package/dist/validation/spec-validation.d.ts +0 -43
  269. package/dist/validation/spec-validation.d.ts.map +0 -1
  270. package/dist/waivers-manager.d.ts +0 -167
  271. package/dist/waivers-manager.d.ts.map +0 -1
  272. package/dist/worktree/worktree-manager.d.ts +0 -54
  273. package/dist/worktree/worktree-manager.d.ts.map +0 -1
@@ -11,6 +11,8 @@ 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
+ const { loadState } = require('../utils/working-state');
14
16
 
15
17
  /**
16
18
  * Load working specification (legacy single file approach)
@@ -30,6 +32,41 @@ async function loadWorkingSpec(specPath = '.caws/working-spec.yaml') {
30
32
  }
31
33
  }
32
34
 
35
+ /**
36
+ * Resolve the primary spec for status display when explicitly targeted
37
+ * or when the resolver can identify a single canonical spec.
38
+ * Returns null when no spec can be auto-selected.
39
+ * @param {Object} options
40
+ * @returns {Promise<Object|null>}
41
+ */
42
+ async function loadResolvedStatusSpec(options = {}) {
43
+ try {
44
+ const resolved = await resolveSpec({
45
+ specId: options.specId,
46
+ specFile: options.spec,
47
+ warnLegacy: false,
48
+ interactive: false,
49
+ quiet: Boolean(options.json),
50
+ });
51
+ return {
52
+ ...resolved,
53
+ selected: true,
54
+ };
55
+ } catch (error) {
56
+ if (
57
+ error.message === 'Spec ID required when multiple specs exist' &&
58
+ !options.specId &&
59
+ !options.spec
60
+ ) {
61
+ return null;
62
+ }
63
+ if (error.message.includes('No CAWS spec found')) {
64
+ return null;
65
+ }
66
+ throw error;
67
+ }
68
+ }
69
+
33
70
  /**
34
71
  * Load specs from the new multi-spec system
35
72
  * @returns {Promise<Array>} Array of spec objects
@@ -317,7 +354,7 @@ function getTimeSince(timestamp) {
317
354
  * @param {Object} data - Status data
318
355
  */
319
356
  function displayStatus(data) {
320
- const { spec, hooks, provenance, waivers, gates } = data;
357
+ const { spec, specSelection, hooks, provenance, waivers, gates } = data;
321
358
 
322
359
  console.log(chalk.bold.cyan('\nCAWS Project Status'));
323
360
  console.log(chalk.cyan('==============================================\n'));
@@ -327,6 +364,9 @@ function displayStatus(data) {
327
364
  console.log(chalk.green('Working Spec'));
328
365
  console.log(chalk.gray(` ID: ${spec.id} | Tier: ${spec.risk_tier} | Mode: ${spec.mode}`));
329
366
  console.log(chalk.gray(` Title: ${spec.title}`));
367
+ if (specSelection?.specType) {
368
+ console.log(chalk.gray(` Source: ${specSelection.specType} (${specSelection.specPath})`));
369
+ }
330
370
  } else {
331
371
  console.log(chalk.red('Working Spec'));
332
372
  console.log(chalk.gray(' No working spec found'));
@@ -335,6 +375,47 @@ function displayStatus(data) {
335
375
 
336
376
  console.log('');
337
377
 
378
+ // Working State
379
+ if (spec && spec.id) {
380
+ let ws = null;
381
+ try { ws = loadState(spec.id); } catch { /* non-fatal */ }
382
+ if (ws && ws.phase !== 'not-started') {
383
+ const phaseLabels = {
384
+ 'not-started': 'Not Started',
385
+ 'spec-authoring': 'Spec Authoring',
386
+ 'implementation': 'Implementation',
387
+ 'verification': 'Verification',
388
+ 'complete': 'Complete',
389
+ };
390
+ console.log(chalk.green('Working State'));
391
+ console.log(chalk.gray(` Phase: ${phaseLabels[ws.phase] || ws.phase}`));
392
+ if (ws.validation) {
393
+ const vIcon = ws.validation.passed ? chalk.green('pass') : chalk.red('fail');
394
+ console.log(chalk.gray(` Validation: ${vIcon}${ws.validation.grade ? ` (Grade ${ws.validation.grade})` : ''}`));
395
+ }
396
+ if (ws.evaluation) {
397
+ console.log(chalk.gray(` Evaluation: ${ws.evaluation.percentage}% (Grade ${ws.evaluation.grade})`));
398
+ }
399
+ if (ws.gates) {
400
+ const gIcon = ws.gates.passed ? chalk.green('pass') : chalk.red('blocked');
401
+ const s = ws.gates.summary;
402
+ console.log(chalk.gray(` Gates: ${gIcon} (${s.passed} pass, ${s.blocked} blocked, ${s.warned} warned)`));
403
+ }
404
+ if (ws.acceptance_criteria) {
405
+ const ac = ws.acceptance_criteria;
406
+ console.log(chalk.gray(` ACs: ${ac.pass}/${ac.total} pass, ${ac.fail} fail, ${ac.unchecked} unchecked`));
407
+ }
408
+ if (ws.files_touched && ws.files_touched.length > 0) {
409
+ console.log(chalk.gray(` Files touched: ${ws.files_touched.length}`));
410
+ }
411
+ if (ws.blockers && ws.blockers.length > 0) {
412
+ console.log(chalk.red(` Blockers: ${ws.blockers.length}`));
413
+ ws.blockers.forEach(b => console.log(chalk.red(` - ${b.message}`)));
414
+ }
415
+ console.log('');
416
+ }
417
+ }
418
+
338
419
  // Git Hooks Status
339
420
  if (hooks.installed) {
340
421
  console.log(chalk.green(`Git Hooks`));
@@ -894,14 +975,23 @@ async function statusCommand(options = {}) {
894
975
  const currentMode = await modes.getCurrentMode();
895
976
 
896
977
  // 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(),
978
+ const [resolvedSpec, specs, hooks, provenance, waivers, gates] = await parallel([
979
+ loadResolvedStatusSpec(options),
980
+ loadSpecsFromMultiSpec(),
981
+ checkGitHooks(),
982
+ loadProvenanceChain(),
983
+ loadWaiverStatus(),
984
+ checkQualityGates(),
904
985
  ]);
986
+ const spec = resolvedSpec?.spec || null;
987
+ const specSelection = resolvedSpec
988
+ ? {
989
+ specPath: resolvedSpec.path
990
+ ? path.relative(process.cwd(), resolvedSpec.path)
991
+ : null,
992
+ specType: resolvedSpec.type,
993
+ }
994
+ : null;
905
995
 
906
996
  // Display status (visual mode if requested)
907
997
  if (options.visual || options.json) {
@@ -932,6 +1022,8 @@ async function statusCommand(options = {}) {
932
1022
  title: spec.title,
933
1023
  riskTier: spec.risk_tier,
934
1024
  mode: spec.mode,
1025
+ specType: specSelection?.specType || null,
1026
+ specPath: specSelection?.specPath || null,
935
1027
  acceptanceCriteria: spec.acceptance_criteria?.length || 0,
936
1028
  completedCriteria:
937
1029
  spec.acceptance_criteria?.filter((c) => c.completed).length || 0,
@@ -960,8 +1052,10 @@ async function statusCommand(options = {}) {
960
1052
  passed: gates.passed,
961
1053
  message: gates.message,
962
1054
  },
1055
+ workingState: spec && spec.id ? (loadState(spec.id) || null) : null,
963
1056
  overallProgress: calculateOverallProgress({
964
1057
  spec,
1058
+ specSelection,
965
1059
  specs,
966
1060
  hooks,
967
1061
  provenance,
@@ -971,6 +1065,7 @@ async function statusCommand(options = {}) {
971
1065
  };
972
1066
 
973
1067
  console.log(JSON.stringify(result, null, 2));
1068
+ return result;
974
1069
  } else {
975
1070
  // Visual output
976
1071
  await displayVisualStatus(
@@ -989,6 +1084,7 @@ async function statusCommand(options = {}) {
989
1084
  // Original text-based output
990
1085
  displayStatus({
991
1086
  spec,
1087
+ specSelection,
992
1088
  hooks,
993
1089
  provenance,
994
1090
  waivers,
@@ -996,6 +1092,13 @@ async function statusCommand(options = {}) {
996
1092
  });
997
1093
  }
998
1094
 
1095
+ if (options.json) {
1096
+ return {
1097
+ command: 'status',
1098
+ mode: 'json',
1099
+ };
1100
+ }
1101
+
999
1102
  const result = outputResult({
1000
1103
  command: 'status',
1001
1104
  mode: options.visual ? 'visual' : options.json ? 'json' : 'text',
@@ -1027,13 +1130,14 @@ async function statusCommand(options = {}) {
1027
1130
  return result;
1028
1131
  },
1029
1132
  'status check',
1030
- true
1133
+ !options.json
1031
1134
  );
1032
1135
  }
1033
1136
 
1034
1137
  module.exports = {
1035
1138
  statusCommand,
1036
1139
  loadWorkingSpec,
1140
+ loadResolvedStatusSpec,
1037
1141
  checkGitHooks,
1038
1142
  loadProvenanceChain,
1039
1143
  loadWaiverStatus,
@@ -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,13 @@ 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 { lifecycle, EVENTS } = require('../utils/lifecycle-events');
18
24
 
19
25
  /**
20
26
  * Validate command handler
@@ -75,12 +81,14 @@ async function validateCommand(specFile, options = {}) {
75
81
  // Check scope conflicts (if multiple specs exist)
76
82
  const { checkMultiSpecStatus } = require('../utils/spec-resolver');
77
83
  const multiSpecStatus = await checkMultiSpecStatus();
84
+ const registry =
85
+ multiSpecStatus.registry ||
86
+ await loadSpecsRegistry();
87
+ const specIds = Object.keys(registry.specs || {});
78
88
 
79
- if (multiSpecStatus.specCount > 1) {
89
+ if (specIds.length > 1) {
80
90
  const { checkScopeConflicts } = require('../utils/spec-resolver');
81
- const conflicts = await checkScopeConflicts(
82
- Object.keys(multiSpecStatus.registry?.specs || {})
83
- );
91
+ const conflicts = await checkScopeConflicts(specIds);
84
92
 
85
93
  if (conflicts.length > 0) {
86
94
  const myConflicts = conflicts.filter((c) => c.spec1 === spec.id || c.spec2 === spec.id);
@@ -135,6 +143,40 @@ async function validateCommand(specFile, options = {}) {
135
143
 
136
144
  const finalResult = enhancedValidation;
137
145
 
146
+ // Record to working state
147
+ try {
148
+ const grade = finalResult.complianceScore !== undefined
149
+ ? getComplianceGrade(finalResult.complianceScore)
150
+ : null;
151
+ recordValidation(spec.id, {
152
+ passed: finalResult.valid,
153
+ compliance_score: finalResult.complianceScore ?? null,
154
+ grade,
155
+ error_count: (finalResult.errors || []).length,
156
+ warning_count: (finalResult.warnings || []).length,
157
+ });
158
+ } catch { /* non-fatal */ }
159
+
160
+ // Emit lifecycle event
161
+ try {
162
+ const grade = finalResult.complianceScore !== undefined
163
+ ? getComplianceGrade(finalResult.complianceScore)
164
+ : null;
165
+ if (finalResult.valid) {
166
+ lifecycle.emit(EVENTS.VALIDATION_PASSED, {
167
+ specId: spec.id, grade, complianceScore: finalResult.complianceScore,
168
+ timestamp: new Date().toISOString(),
169
+ });
170
+ } else {
171
+ lifecycle.emit(EVENTS.VALIDATION_FAILED, {
172
+ specId: spec.id, errors: finalResult.errors || [],
173
+ errorCount: (finalResult.errors || []).length,
174
+ warningCount: (finalResult.warnings || []).length,
175
+ timestamp: new Date().toISOString(),
176
+ });
177
+ }
178
+ } catch { /* non-fatal */ }
179
+
138
180
  // Format output based on requested format
139
181
  if (options.format === 'json') {
140
182
  // Structured JSON output matching CAWSValidationResult
@@ -236,7 +278,8 @@ async function validateCommand(specFile, options = {}) {
236
278
  if (error.message === 'Spec ID required when multiple specs exist' && !options.specId) {
237
279
  const { checkMultiSpecStatus } = require('../utils/spec-resolver');
238
280
  const status = await checkMultiSpecStatus();
239
- const specIds = Object.keys(status.registry?.specs || {});
281
+ const registry = status.registry || await loadSpecsRegistry();
282
+ const specIds = Object.keys(registry.specs || {});
240
283
 
241
284
  if (specIds.length === 0) {
242
285
  console.error(chalk.red('No specs found in registry'));
@@ -7,13 +7,11 @@
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');
17
15
 
18
16
  /**
19
17
  * Detect the project's test runner from config files.
@@ -318,55 +316,6 @@ function verifySpec(spec, projectRoot, options = {}) {
318
316
  };
319
317
  }
320
318
 
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
319
  /**
371
320
  * Main command handler
372
321
  */
@@ -379,35 +328,39 @@ async function verifyAcsCommand(options = {}) {
379
328
  process.exit(1);
380
329
  }
381
330
 
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
- }
331
+ const resolved = await resolveSpec({
332
+ specId: options.specId,
333
+ warnLegacy: false,
334
+ });
388
335
 
389
336
  const allResults = [];
390
337
  let totalAcs = 0, totalMechanical = 0, totalPass = 0, totalFail = 0, totalUnchecked = 0;
391
-
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);
401
-
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
- }
338
+ const result = verifySpec(resolved.spec, projectRoot, {
339
+ run: options.run || false,
340
+ runner: options.runner,
341
+ });
342
+ result.path = resolved.path;
343
+ result.type = resolved.type;
344
+ allResults.push(result);
345
+
346
+ for (const r of result.results) {
347
+ totalAcs++;
348
+ if (r.status === 'PASS') { totalPass++; totalMechanical++; }
349
+ else if (r.status === 'FAIL') { totalFail++; totalMechanical++; }
350
+ else { totalUnchecked++; }
409
351
  }
410
352
 
353
+ // Record to working state
354
+ try {
355
+ recordACVerification(resolved.spec.id, {
356
+ total: totalAcs,
357
+ pass: totalPass,
358
+ fail: totalFail,
359
+ unchecked: totalUnchecked,
360
+ results: result.results,
361
+ }, projectRoot);
362
+ } catch { /* non-fatal */ }
363
+
411
364
  // Output
412
365
  if (options.format === 'json') {
413
366
  console.log(JSON.stringify({
@@ -421,7 +374,11 @@ async function verifyAcsCommand(options = {}) {
421
374
  // Table output
422
375
  for (const result of allResults) {
423
376
  console.log(chalk.cyan(`\n## ${result.specId} — ${result.title}`));
424
- console.log(chalk.gray(` Test runner: ${result.runner} | Mode: ${options.run ? 'run' : 'collect-only'}`));
377
+ console.log(
378
+ chalk.gray(
379
+ ` Test runner: ${result.runner} | Mode: ${options.run ? 'run' : 'collect-only'} | Spec: ${result.type} -> ${result.path}`
380
+ )
381
+ );
425
382
  console.log();
426
383
 
427
384
  const widths = { id: 8, desc: 40, method: 14, status: 10 };
@@ -13,6 +13,7 @@ const path = require('path');
13
13
  const yaml = require('js-yaml');
14
14
  const chalk = require('chalk');
15
15
  const { initializeGlobalSetup } = require('../config');
16
+ const { getAgentSessionId } = require('../utils/agent-session');
16
17
  const WaiversManager = require('../waivers-manager');
17
18
  const { commandWrapper, Output } = require('../utils/command-wrapper');
18
19
 
@@ -24,16 +25,12 @@ const WAIVER_DIR = '.caws/waivers';
24
25
  * within the check-*.mjs files.
25
26
  */
26
27
  const VALID_GATES = [
27
- 'naming',
28
- 'code_freeze',
29
- 'duplication',
30
- 'duplication_gate',
31
- 'god_objects',
32
- 'placeholders',
33
- 'simplification',
34
- 'hidden-todo',
35
- 'documentation',
36
- '*',
28
+ 'budget_limit',
29
+ 'spec_completeness',
30
+ 'scope_boundary',
31
+ 'god_object',
32
+ 'todo_detection',
33
+ '*', // wildcard — waiver applies to all gates
37
34
  ];
38
35
 
39
36
  /**
@@ -49,7 +46,7 @@ async function waiversCommand(subcommand = 'list', options = {}) {
49
46
  const setup = initializeGlobalSetup();
50
47
 
51
48
  if (setup.hasWorkingSpec) {
52
- Output.success(`Detected ${setup.setupType} CAWS setup`, {
49
+ Output.success(`Detected ${setup.type} CAWS setup`, {
53
50
  capabilities: setup.capabilities,
54
51
  });
55
52
  }
@@ -138,6 +135,24 @@ async function createWaiver(options) {
138
135
  process.exit(1);
139
136
  }
140
137
 
138
+ // Self-approval prevention: creator cannot be approver
139
+ // Uses strict equality — the previous .includes() check was an asymmetric
140
+ // substring match that produced false positives (blocking legitimate approvers
141
+ // whose name happened to contain the session ID) while missing the reverse
142
+ // case (approver is a prefix of the session ID).
143
+ // When CLAUDE_SESSION_ID is unset or empty, we can't identify the creator,
144
+ // so self-approval prevention is skipped ('' || null → null → falsy guard).
145
+ const creatorSession = getAgentSessionId(process.cwd());
146
+ if (creatorSession && options.approvedBy) {
147
+ if (options.approvedBy === creatorSession) {
148
+ throw new Error(
149
+ 'Waiver creator cannot be the approver.\n' +
150
+ 'A different agent or human must approve this waiver.\n' +
151
+ `Creator session: ${creatorSession}`
152
+ );
153
+ }
154
+ }
155
+
141
156
  // Generate waiver ID
142
157
  const waiverId = `WV-${Date.now().toString().slice(-4)}`;
143
158
  const timestamp = new Date().toISOString();
@@ -155,8 +170,27 @@ async function createWaiver(options) {
155
170
  impact_level: options.impactLevel,
156
171
  mitigation_plan: options.mitigationPlan,
157
172
  status: 'active',
173
+ created_by_session: creatorSession,
158
174
  };
159
175
 
176
+ // Validate waiver against schema before persisting
177
+ try {
178
+ const { createValidator, getSchemaPath } = require('../utils/schema-validator');
179
+ const schemaPath = getSchemaPath('waivers.schema.json', process.cwd());
180
+ const validate = createValidator(schemaPath);
181
+ // waivers.schema.json uses patternProperties keyed by waiver ID
182
+ const waiverDoc = { [waiverId]: waiver };
183
+ const result = validate(waiverDoc);
184
+ if (!result.valid) {
185
+ console.warn(chalk.yellow('Waiver has schema violations:'));
186
+ result.errors.forEach((err) => {
187
+ console.warn(chalk.yellow(` ${err.instancePath}: ${err.message}`));
188
+ });
189
+ }
190
+ } catch (schemaErr) {
191
+ // Non-fatal — don't block waiver creation on schema issues
192
+ }
193
+
160
194
  // Save individual waiver file
161
195
  const waiverPath = path.join(process.cwd(), WAIVER_DIR, `${waiverId}.yaml`);
162
196
  fs.writeFileSync(waiverPath, yaml.dump(waiver, { lineWidth: -1 }));
@@ -198,9 +232,32 @@ async function listWaivers(_options) {
198
232
  return;
199
233
  }
200
234
 
235
+ // Load a schema validator once for all waiver files (if available)
236
+ let waiverValidate = null;
237
+ try {
238
+ const { createValidator, getSchemaPath } = require('../utils/schema-validator');
239
+ const schemaPath = getSchemaPath('waivers.schema.json', process.cwd());
240
+ waiverValidate = createValidator(schemaPath);
241
+ } catch {
242
+ // Schema not available — skip validation
243
+ }
244
+
201
245
  const waivers = waiverFiles.map((file) => {
202
246
  const content = fs.readFileSync(path.join(waiversDir, file), 'utf8');
203
- return yaml.load(content);
247
+ const waiver = yaml.load(content);
248
+
249
+ // Validate each loaded waiver against schema
250
+ if (waiverValidate && waiver && waiver.id) {
251
+ const result = waiverValidate({ [waiver.id]: waiver });
252
+ if (!result.valid) {
253
+ console.warn(chalk.yellow(`Schema warning for ${file}:`));
254
+ result.errors.forEach((err) => {
255
+ console.warn(chalk.yellow(` ${err.instancePath}: ${err.message}`));
256
+ });
257
+ }
258
+ }
259
+
260
+ return waiver;
204
261
  });
205
262
 
206
263
  // Filter by status