@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
@@ -279,7 +279,7 @@ if [ -d ".caws" ]; then
279
279
  var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
280
280
  var wts = Object.values(reg.worktrees || {});
281
281
  var active = wts.filter(function(w) {
282
- return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
282
+ return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
283
283
  });
284
284
  console.log(active.length + ':' + active.map(function(w) { return w.name; }).join(','));
285
285
  } catch(e) { console.log('0:'); }
@@ -338,7 +338,7 @@ if [ -d ".caws" ]; then
338
338
  HAS_ACTIVE_WT=$(node -e "
339
339
  try {
340
340
  var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
341
- var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
341
+ var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
342
342
  console.log(active.length > 0 ? 'yes' : 'no');
343
343
  } catch(e) { console.log('no'); }
344
344
  " 2>/dev/null)
@@ -358,110 +358,42 @@ if [ -d ".caws" ]; then
358
358
  fi
359
359
  # ===== End Multi-Agent Safety Guard =====
360
360
 
361
- # Fallback chain for quality gates:
362
- # 1. Try Node.js script (if exists)
363
- # 2. Try CAWS CLI
364
- # 3. Try Makefile target
365
- # 4. Try Python scripts
366
- # 5. Skip gracefully (warn only)
367
-
361
+ # Run CAWS quality gates
368
362
  QUALITY_GATES_RAN=false
363
+ QUALITY_GATES_WARNED=false
369
364
 
370
- # Option 1: Quality gates package (installed via npm)
371
- if [ -f "node_modules/@paths.design/quality-gates/run-quality-gates.mjs" ]; then
372
- if command -v node >/dev/null 2>&1; then
373
- echo "Running quality gates package..."
374
- if CI= node node_modules/@paths.design/quality-gates/run-quality-gates.mjs --context=commit; then
375
- echo "Quality gates passed"
376
- QUALITY_GATES_RAN=true
377
- else
378
- echo "Quality gates failed - commit blocked"
379
- echo "Fix the violations above before committing"
380
- exit 1
381
- fi
382
- fi
383
- # Option 1b: Quality gates package (monorepo/local copy)
384
- elif [ -f "node_modules/@caws/quality-gates/run-quality-gates.mjs" ]; then
385
- if command -v node >/dev/null 2>&1; then
386
- echo "Running quality gates package (local)..."
387
- if CI= node node_modules/@caws/quality-gates/run-quality-gates.mjs --context=commit; then
388
- echo "Quality gates passed"
389
- QUALITY_GATES_RAN=true
390
- else
391
- echo "Quality gates failed - commit blocked"
392
- echo "Fix the violations above before committing"
393
- exit 1
394
- fi
395
- fi
396
- # Option 2: Legacy Node.js quality gates script (deprecated)
397
- elif [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
398
- if command -v node >/dev/null 2>&1; then
399
- echo "Running legacy Node.js quality gates script..."
400
- if node scripts/quality-gates/run-quality-gates.js; then
401
- echo "Quality gates passed"
402
- QUALITY_GATES_RAN=true
403
- else
404
- echo "Quality gates failed - commit blocked"
405
- echo "Fix the violations above before committing"
406
- exit 1
407
- fi
408
- fi
409
- # Option 3: CAWS CLI validation
410
- elif command -v caws >/dev/null 2>&1; then
411
- # In a worktree, validate only the associated spec to avoid false positives
412
- CAWS_VALIDATE_ARGS="--quiet"
413
- WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
414
- if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
415
- SPEC_ID=$(node -e "
416
- try {
417
- var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
418
- var wt = Object.values(reg.worktrees || {}).find(function(w) {
419
- return w.branch === '$WORKTREE_BRANCH';
420
- });
421
- if (wt && wt.specId) console.log(wt.specId);
422
- } catch(e) {}
423
- " 2>/dev/null || echo "")
424
- if [ -n "$SPEC_ID" ]; then
425
- CAWS_VALIDATE_ARGS="--quiet --spec-id $SPEC_ID"
426
- fi
427
- fi
428
- echo "Running CAWS CLI validation..."
429
- if caws validate $CAWS_VALIDATE_ARGS 2>/dev/null; then
430
- echo "CAWS validation passed"
431
- QUALITY_GATES_RAN=true
432
- else
433
- echo "CAWS validation failed, but allowing commit (non-blocking)"
434
- echo "Run 'caws validate' for details"
435
- QUALITY_GATES_RAN=true
436
- fi
437
- # Option 3: Makefile target
438
- elif [ -f "Makefile" ] && grep -q "caws-validate\\|caws-gates" Makefile; then
439
- echo "Running Makefile quality gates..."
440
- if make caws-validate >/dev/null 2>&1 || make caws-gates >/dev/null 2>&1; then
441
- echo "Makefile quality gates passed"
442
- QUALITY_GATES_RAN=true
443
- else
444
- echo "Makefile quality gates failed, but allowing commit (non-blocking)"
445
- QUALITY_GATES_RAN=true
446
- fi
447
- # Option 4: Python scripts
448
- elif [ -f "scripts/simple_gates.py" ] && command -v python3 >/dev/null 2>&1; then
449
- echo "Running Python quality gates script..."
450
- if python3 scripts/simple_gates.py all --tier 2 --profile backend-api >/dev/null 2>&1; then
451
- echo "Python quality gates passed"
365
+ # Resolve spec ID from worktree context if available
366
+ SPEC_ID=""
367
+ WORKTREE_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
368
+ if [ -f ".caws/worktrees.json" ] && command -v node >/dev/null 2>&1; then
369
+ SPEC_ID=$(node -e "
370
+ try {
371
+ var reg = JSON.parse(require('fs').readFileSync('.caws/worktrees.json', 'utf8'));
372
+ var wt = Object.values(reg.worktrees || {}).find(function(w) {
373
+ return w.branch === '$WORKTREE_BRANCH';
374
+ });
375
+ if (wt && wt.specId) console.log(wt.specId);
376
+ } catch(e) {}
377
+ " 2>/dev/null || echo "")
378
+ fi
379
+
380
+ GATES_ARGS="--context=commit --quiet"
381
+ if [ -n "$SPEC_ID" ]; then
382
+ GATES_ARGS="$GATES_ARGS --spec-id $SPEC_ID"
383
+ fi
384
+
385
+ if command -v caws >/dev/null 2>&1; then
386
+ if caws gates run $GATES_ARGS; then
387
+ echo "Quality gates passed"
452
388
  QUALITY_GATES_RAN=true
453
389
  else
454
- echo "Python quality gates failed, but allowing commit (non-blocking)"
455
- QUALITY_GATES_RAN=true
390
+ GATE_EXIT=$?
391
+ echo "Quality gates BLOCKED the commit (exit code $GATE_EXIT)"
392
+ exit 1
456
393
  fi
457
- # Option 5: Skip gracefully
458
394
  else
459
- echo "Quality gates not available - skipping"
460
- echo "Available options:"
461
- echo " - Install quality gates: npm install --save-dev @paths.design/quality-gates"
462
- echo " - Install CAWS CLI: npm install -g @paths.design/caws-cli"
463
- echo " - Use Python: python3 scripts/simple_gates.py"
464
- echo " - Use Makefile: make caws-gates"
395
+ echo "CAWS CLI not available skipping quality gates"
396
+ echo "Install with: npm install -g @paths.design/caws-cli"
465
397
  QUALITY_GATES_RAN=true
466
398
  fi
467
399
 
@@ -534,7 +466,11 @@ ${todoSuggestion
534
466
  fi
535
467
  fi
536
468
 
537
- echo "All quality checks passed - proceeding with commit"
469
+ if [ "$QUALITY_GATES_WARNED" = true ]; then
470
+ echo "Proceeding with commit (some quality checks had warnings)"
471
+ else
472
+ echo "All quality checks passed - proceeding with commit"
473
+ fi
538
474
  exit 0
539
475
  `;
540
476
  }
@@ -554,6 +490,13 @@ function generatePostCommitHook() {
554
490
  exit 0
555
491
  fi
556
492
 
493
+ # Skip during merge operations — caws worktree merge triggers a merge
494
+ # commit, and writing to .caws/provenance/chain.json here would dirty
495
+ # the tree mid-merge, blocking subsequent operations.
496
+ if [ -f ".git/MERGE_HEAD" ] || [ -f "$(git rev-parse --git-dir 2>/dev/null)/MERGE_HEAD" ]; then
497
+ exit 0
498
+ fi
499
+
557
500
  # Get the current commit hash
558
501
  COMMIT_HASH=$(git rev-parse HEAD)
559
502
 
@@ -828,7 +771,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
828
771
  try {
829
772
  var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
830
773
  var active = Object.values(reg.worktrees || {}).filter(function(w) {
831
- return w.status === 'active' && w.baseBranch === '$CURRENT_BRANCH';
774
+ return (w.status === 'active' || w.status === 'fresh' || w.status === 'merged') && w.baseBranch === '$CURRENT_BRANCH';
832
775
  });
833
776
  console.log(active.length);
834
777
  } catch(e) { console.log('0'); }
@@ -728,11 +728,12 @@ async function scaffoldProject(options) {
728
728
  } else {
729
729
  console.log('2. Run: caws validate (verify setup)');
730
730
  console.log('3. Run: caws diagnose (check project health)');
731
- console.log('4. Customize .caws/working-spec.yaml for your project');
732
- console.log('5. Optional: Create .caws/policy.yaml for tier-specific budgets');
731
+ console.log('4. Customize .caws/specs/<spec-id>.yaml for your project');
732
+ console.log('5. Treat .caws/working-spec.yaml as a compatibility mirror');
733
+ console.log('6. Optional: Create .caws/policy.yaml for tier-specific budgets');
733
734
  if (!qualityGatesAdded && !options.minimal) {
734
735
  console.log(
735
- chalk.gray('6. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
736
+ chalk.gray('7. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
736
737
  );
737
738
  console.log(
738
739
  chalk.gray(
@@ -11,6 +11,9 @@ const fs = require('fs-extra');
11
11
  const path = require('path');
12
12
  const crypto = require('crypto');
13
13
 
14
+ const { mergeFilesTouched } = require('../utils/working-state');
15
+ const { appendEventSync } = require('../utils/event-log');
16
+
14
17
  const SESSIONS_DIR = '.caws/sessions';
15
18
  const REGISTRY_FILE = '.caws/sessions.json';
16
19
  const CAPSULE_SCHEMA_VERSION = 'caws.capsule.v1';
@@ -79,16 +82,62 @@ function getWorkspaceFingerprint(cwd) {
79
82
  }
80
83
 
81
84
  /**
82
- * Get project name from working spec or directory
85
+ * Load the best available spec synchronously (feature specs first, then legacy).
83
86
  * @param {string} root - Repository root
87
+ * @param {string} [specId] - Optional specific spec ID
88
+ * @returns {object|null} Parsed spec object or null
89
+ */
90
+ function loadBestSpecSync(root, specId) {
91
+ const yaml = require('js-yaml');
92
+
93
+ // If a specific spec ID is requested, load it directly
94
+ if (specId) {
95
+ for (const ext of ['.yaml', '.yml']) {
96
+ const p = path.join(root, '.caws/specs', `${specId}${ext}`);
97
+ if (fs.existsSync(p)) {
98
+ try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
99
+ }
100
+ }
101
+ }
102
+
103
+ // Check registry for active feature specs
104
+ const registryPath = path.join(root, '.caws/specs/registry.json');
105
+ if (fs.existsSync(registryPath)) {
106
+ try {
107
+ const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
108
+ const specs = registry.specs || {};
109
+ const activeIds = Object.keys(specs).filter(
110
+ id => specs[id].status !== 'closed' && specs[id].status !== 'archived'
111
+ );
112
+ for (const id of activeIds) {
113
+ const specEntry = specs[id];
114
+ const p = path.join(root, '.caws/specs', specEntry.path || `${id}.yaml`);
115
+ if (fs.existsSync(p)) {
116
+ try { return yaml.load(fs.readFileSync(p, 'utf8')); } catch { /* skip */ }
117
+ }
118
+ }
119
+ } catch { /* fall through */ }
120
+ }
121
+
122
+ // Legacy fallback
123
+ const legacyPath = path.join(root, '.caws/working-spec.yaml');
124
+ if (fs.existsSync(legacyPath)) {
125
+ try { return yaml.load(fs.readFileSync(legacyPath, 'utf8')); } catch { /* skip */ }
126
+ }
127
+
128
+ return null;
129
+ }
130
+
131
+ /**
132
+ * Get project name from best available spec or directory
133
+ * @param {string} root - Repository root
134
+ * @param {string} [specId] - Optional specific spec ID
84
135
  * @returns {string}
85
136
  */
86
- function getProjectName(root) {
137
+ function getProjectName(root, specId) {
87
138
  try {
88
- const yaml = require('js-yaml');
89
- const specPath = path.join(root, '.caws/working-spec.yaml');
90
- if (fs.existsSync(specPath)) {
91
- const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
139
+ const spec = loadBestSpecSync(root, specId);
140
+ if (spec) {
92
141
  return spec.title || spec.id || path.basename(root);
93
142
  }
94
143
  } catch {
@@ -98,16 +147,15 @@ function getProjectName(root) {
98
147
  }
99
148
 
100
149
  /**
101
- * Get skein ID from working spec
150
+ * Get skein ID from best available spec
102
151
  * @param {string} root - Repository root
152
+ * @param {string} [specId] - Optional specific spec ID
103
153
  * @returns {string}
104
154
  */
105
- function getSkeinId(root) {
155
+ function getSkeinId(root, specId) {
106
156
  try {
107
- const yaml = require('js-yaml');
108
- const specPath = path.join(root, '.caws/working-spec.yaml');
109
- if (fs.existsSync(specPath)) {
110
- const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
157
+ const spec = loadBestSpecSync(root, specId);
158
+ if (spec) {
111
159
  return spec.id || 'unknown';
112
160
  }
113
161
  } catch {
@@ -201,8 +249,8 @@ function startSession(options = {}) {
201
249
 
202
250
  const capsule = {
203
251
  schema: CAPSULE_SCHEMA_VERSION,
204
- project: getProjectName(root),
205
- skein_id: getSkeinId(root),
252
+ project: getProjectName(root, specId),
253
+ skein_id: getSkeinId(root, specId),
206
254
  session_id: sessionId,
207
255
  role,
208
256
  spec_id: specId || null,
@@ -250,6 +298,22 @@ function startSession(options = {}) {
250
298
  };
251
299
  saveRegistry(root, registry);
252
300
 
301
+ // EVLOG-001: emit session_started event via the sync append path.
302
+ // spec_id is optional for this event type; we include it only when
303
+ // the session is bound to a spec.
304
+ const sessionStartedEvent = {
305
+ actor: 'session',
306
+ event: 'session_started',
307
+ data: {
308
+ session_id: sessionId,
309
+ role,
310
+ branch: capsule.base_state.branch,
311
+ head_rev: capsule.base_state.head_rev,
312
+ },
313
+ };
314
+ if (specId) sessionStartedEvent.spec_id = specId;
315
+ appendEventSync(sessionStartedEvent, { projectRoot: root, session_id: sessionId });
316
+
253
317
  return capsule;
254
318
  }
255
319
 
@@ -318,6 +382,11 @@ function checkpointSession(data = {}) {
318
382
  // Write updated capsule
319
383
  fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
320
384
 
385
+ // Bridge to working state (per-spec)
386
+ if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
387
+ try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
388
+ }
389
+
321
390
  return capsule;
322
391
  }
323
392
 
@@ -385,11 +454,33 @@ function endSession(data = {}) {
385
454
  // Write finalized capsule
386
455
  fs.writeFileSync(capsulePath, JSON.stringify(capsule, null, 2));
387
456
 
457
+ // Bridge to working state (per-spec)
458
+ if (capsule.spec_id && capsule.work_summary.paths_touched.length > 0) {
459
+ try { mergeFilesTouched(capsule.spec_id, capsule.work_summary.paths_touched, root); } catch { /* non-fatal */ }
460
+ }
461
+
388
462
  // Update registry
389
463
  registry.sessions[sessionId].status = 'completed';
390
464
  registry.sessions[sessionId].ended_at = capsule.ended_at;
391
465
  saveRegistry(root, registry);
392
466
 
467
+ // EVLOG-001: emit session_ended event with final files_touched list.
468
+ // The renderer uses this to merge file touches into the spec view.
469
+ const sessionEndedEvent = {
470
+ actor: 'session',
471
+ event: 'session_ended',
472
+ data: {
473
+ session_id: sessionId,
474
+ files_touched: capsule.work_summary.paths_touched || [],
475
+ outcome: capsule.known_issues.some((i) => i.type === 'error') ? 'error' : 'success',
476
+ },
477
+ };
478
+ if (capsule.spec_id) {
479
+ sessionEndedEvent.spec_id = capsule.spec_id;
480
+ sessionEndedEvent.data.spec_id = capsule.spec_id;
481
+ }
482
+ appendEventSync(sessionEndedEvent, { projectRoot: root, session_id: sessionId });
483
+
393
484
  return capsule;
394
485
  }
395
486
 
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview Sidecar Registry
3
+ * Central registry of all bounded governance sidecars.
4
+ * @author @darianrosebrook
5
+ */
6
+
7
+ const { analyzeSpecDrift } = require('./spec-drift');
8
+ const { diagnoseQualityGaps } = require('./quality-gaps');
9
+ const { draftWaiver } = require('./waiver-draft');
10
+ const { summarizeProvenance } = require('./provenance-summary');
11
+ const { createSidecarOutput, createNoStateOutput, formatSidecarText } = require('./schema');
12
+
13
+ /**
14
+ * Registry of available sidecars.
15
+ * Each entry has a function and a short description for help text.
16
+ */
17
+ const SIDECARS = {
18
+ drift: { fn: analyzeSpecDrift, description: 'Analyze spec drift vs implementation evidence' },
19
+ gaps: { fn: diagnoseQualityGaps, description: 'Diagnose quality gaps preventing phase advancement' },
20
+ 'waiver-draft': { fn: draftWaiver, description: 'Generate pre-filled waiver templates from gate failures' },
21
+ provenance: { fn: summarizeProvenance, description: 'Summarize work provenance for merge readiness' },
22
+ };
23
+
24
+ module.exports = {
25
+ SIDECARS,
26
+ analyzeSpecDrift,
27
+ diagnoseQualityGaps,
28
+ draftWaiver,
29
+ summarizeProvenance,
30
+ createSidecarOutput,
31
+ createNoStateOutput,
32
+ formatSidecarText,
33
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @fileoverview Sidecar Lifecycle Listeners
3
+ * Registers lightweight event handlers that print hints to run sidecars
4
+ * at governance-significant moments. Non-blocking, non-fatal.
5
+ * @author @darianrosebrook
6
+ */
7
+
8
+ const { lifecycle, EVENTS } = require('../utils/lifecycle-events');
9
+
10
+ /**
11
+ * Register sidecar hint listeners on the lifecycle emitter.
12
+ * Call once during CLI startup.
13
+ */
14
+ function registerSidecarListeners() {
15
+ // On gate blocked → suggest waiver draft
16
+ lifecycle.on(EVENTS.GATES_BLOCKED, (payload) => {
17
+ try {
18
+ if (process.env.CAWS_QUIET === '1') return;
19
+ const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
20
+ const gateFlag = payload.gateName ? ` --gate ${payload.gateName}` : '';
21
+ console.log(
22
+ `\n [Sidecar] Waiver draft available: caws sidecar waiver-draft${specFlag}${gateFlag}`
23
+ );
24
+ } catch { /* non-fatal */ }
25
+ });
26
+
27
+ // On phase transition (non-complete) → suggest quality gap analysis
28
+ lifecycle.on(EVENTS.PHASE_TRANSITION, (payload) => {
29
+ try {
30
+ if (process.env.CAWS_QUIET === '1') return;
31
+ if (payload.newPhase === 'complete') return;
32
+ const specFlag = payload.specId ? ` --spec-id ${payload.specId}` : '';
33
+ console.log(
34
+ `\n [Sidecar] Phase changed to ${payload.newPhase}. Run: caws sidecar gaps${specFlag}`
35
+ );
36
+ } catch { /* non-fatal */ }
37
+ });
38
+ }
39
+
40
+ module.exports = { registerSidecarListeners };