@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
@@ -0,0 +1,173 @@
1
+ #!/bin/bash
2
+ # Document Frontmatter Check Hook for Claude Code
3
+ # Warns when docs/**/*.md files are written/edited without proper frontmatter.
4
+ # Advisory only — does not block.
5
+ #
6
+ # Validates YAML frontmatter with required fields, authority/status enums,
7
+ # governs requirements for high-authority docs, and verified_at_commit for
8
+ # implementation-state claims.
9
+
10
+ set -euo pipefail
11
+
12
+ INPUT=$(cat)
13
+
14
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
15
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
16
+
17
+ # Only check Write and Edit tools
18
+ if [[ "$TOOL_NAME" != "Write" ]] && [[ "$TOOL_NAME" != "Edit" ]]; then
19
+ exit 0
20
+ fi
21
+
22
+ if [[ -z "$FILE_PATH" ]]; then
23
+ exit 0
24
+ fi
25
+
26
+ # Only check .md files under docs/
27
+ if [[ ! "$FILE_PATH" =~ docs/.*\.md$ ]]; then
28
+ exit 0
29
+ fi
30
+
31
+ # Skip exempt filenames
32
+ BASENAME=$(basename "$FILE_PATH")
33
+ if [[ "$BASENAME" == "README.md" ]] || [[ "$BASENAME" == "INDEX.md" ]] || [[ "$BASENAME" == "index.md" ]] || [[ "$BASENAME" == "00_INDEX.md" ]]; then
34
+ exit 0
35
+ fi
36
+
37
+ # Skip archive and templates directories
38
+ if [[ "$FILE_PATH" =~ docs/archive/ ]] || [[ "$FILE_PATH" =~ docs/templates/ ]]; then
39
+ exit 0
40
+ fi
41
+
42
+ # Skip ephemeral (gitignored, not governed)
43
+ if [[ "$FILE_PATH" =~ docs/ephemeral/ ]]; then
44
+ exit 0
45
+ fi
46
+
47
+ # Check if file exists (Write creates it, Edit modifies it)
48
+ if [[ ! -f "$FILE_PATH" ]]; then
49
+ exit 0
50
+ fi
51
+
52
+ # --- Frontmatter validation ---
53
+
54
+ # V1: Check for frontmatter delimiters
55
+ FIRST_LINE=$(head -1 "$FILE_PATH" 2>/dev/null || echo "")
56
+ if [[ "$FIRST_LINE" != "---" ]]; then
57
+ echo '{
58
+ "hookSpecificOutput": {
59
+ "hookEventName": "PostToolUse",
60
+ "additionalContext": "Doc governance (V1): '"$FILE_PATH"' is missing YAML frontmatter. All docs under docs/ (except README.md, archive/, templates/) must start with --- delimiters containing doc_id, authority, status, title, owner, and updated fields."
61
+ }
62
+ }'
63
+ exit 0
64
+ fi
65
+
66
+ # Extract frontmatter block (between first and second ---)
67
+ FRONTMATTER=$(awk 'NR==1 && /^---$/{found=1; next} found && /^---$/{exit} found{print}' "$FILE_PATH")
68
+
69
+ if [[ -z "$FRONTMATTER" ]]; then
70
+ echo '{
71
+ "hookSpecificOutput": {
72
+ "hookEventName": "PostToolUse",
73
+ "additionalContext": "Doc governance (V1): '"$FILE_PATH"' has opening --- but no closing --- for frontmatter block."
74
+ }
75
+ }'
76
+ exit 0
77
+ fi
78
+
79
+ # V2: Check required fields
80
+ MISSING=""
81
+ for field in doc_id authority status title owner updated; do
82
+ if ! echo "$FRONTMATTER" | grep -q "^${field}:"; then
83
+ MISSING="${MISSING} ${field}"
84
+ fi
85
+ done
86
+
87
+ if [[ -n "$MISSING" ]]; then
88
+ echo '{
89
+ "hookSpecificOutput": {
90
+ "hookEventName": "PostToolUse",
91
+ "additionalContext": "Doc governance (V2): '"$FILE_PATH"' is missing required frontmatter fields:'"$MISSING"'."
92
+ }
93
+ }'
94
+ exit 0
95
+ fi
96
+
97
+ # V2: Check authority value
98
+ AUTHORITY=$(echo "$FRONTMATTER" | grep "^authority:" | head -1 | sed 's/^authority: *//' | tr -d '"' | tr -d "'")
99
+ case "$AUTHORITY" in
100
+ canonical|policy|architecture|adr|spec|roadmap|reference|working|ephemeral)
101
+ ;;
102
+ *)
103
+ echo '{
104
+ "hookSpecificOutput": {
105
+ "hookEventName": "PostToolUse",
106
+ "additionalContext": "Doc governance (V2): '"$FILE_PATH"' has invalid authority '"'"''"$AUTHORITY"''"'"'. Must be one of: canonical, policy, architecture, adr, spec, roadmap, reference, working, ephemeral."
107
+ }
108
+ }'
109
+ exit 0
110
+ ;;
111
+ esac
112
+
113
+ # V2: Check status value
114
+ STATUS=$(echo "$FRONTMATTER" | grep "^status:" | head -1 | sed 's/^status: *//' | tr -d '"' | tr -d "'")
115
+ case "$STATUS" in
116
+ draft|active|implemented|proven|superseded|archived)
117
+ ;;
118
+ *)
119
+ echo '{
120
+ "hookSpecificOutput": {
121
+ "hookEventName": "PostToolUse",
122
+ "additionalContext": "Doc governance (V2): '"$FILE_PATH"' has invalid status '"'"''"$STATUS"''"'"'. Must be one of: draft, active, implemented, proven, superseded, archived."
123
+ }
124
+ }'
125
+ exit 0
126
+ ;;
127
+ esac
128
+
129
+ # V3: Check governs for high-authority docs
130
+ case "$AUTHORITY" in
131
+ canonical|architecture|adr|spec)
132
+ if ! echo "$FRONTMATTER" | grep -q "^governs:"; then
133
+ echo '{
134
+ "hookSpecificOutput": {
135
+ "hookEventName": "PostToolUse",
136
+ "additionalContext": "Doc governance (V3): '"$FILE_PATH"' has authority '"'"''"$AUTHORITY"''"'"' but no governs section. Docs with authority canonical/architecture/adr/spec must declare what they govern (modules, schemas, or specs)."
137
+ }
138
+ }'
139
+ exit 0
140
+ fi
141
+ ;;
142
+ esac
143
+
144
+ # V4: Check verified_at_commit for implementation-state claims
145
+ case "$STATUS" in
146
+ implemented|proven)
147
+ if ! echo "$FRONTMATTER" | grep -q "^verified_at_commit:"; then
148
+ echo '{
149
+ "hookSpecificOutput": {
150
+ "hookEventName": "PostToolUse",
151
+ "additionalContext": "Doc governance (V4): '"$FILE_PATH"' has status '"'"''"$STATUS"''"'"' but no verified_at_commit. Docs claiming implementation state must declare the commit SHA where claims were verified."
152
+ }
153
+ }'
154
+ exit 0
155
+ fi
156
+ ;;
157
+ esac
158
+
159
+ # V5: Check superseded_by for superseded docs
160
+ if [[ "$STATUS" == "superseded" ]]; then
161
+ if ! echo "$FRONTMATTER" | grep -q "^superseded_by:"; then
162
+ echo '{
163
+ "hookSpecificOutput": {
164
+ "hookEventName": "PostToolUse",
165
+ "additionalContext": "Doc governance (V5): '"$FILE_PATH"' has status '"'"'superseded'"'"' but no superseded_by. Superseded docs must declare their replacement doc_id."
166
+ }
167
+ }'
168
+ exit 0
169
+ fi
170
+ fi
171
+
172
+ # All checks passed
173
+ exit 0
@@ -29,25 +29,39 @@ fi
29
29
  PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
30
30
 
31
31
  # Check if we're in a CAWS project
32
- if [[ ! -f "$PROJECT_DIR/.caws/working-spec.yaml" ]]; then
32
+ if [[ ! -f "$PROJECT_DIR/.caws/working-spec.yaml" ]] && [[ ! -d "$PROJECT_DIR/.caws/specs" ]]; then
33
33
  exit 0
34
34
  fi
35
35
 
36
36
  # Check if CAWS CLI is available
37
37
  if ! command -v caws &> /dev/null; then
38
- # Suggest installing CAWS
39
38
  echo '{
40
39
  "hookSpecificOutput": {
41
40
  "hookEventName": "PostToolUse",
42
- "additionalContext": "CAWS CLI not available. Consider installing with: npm install -g @caws/cli"
41
+ "additionalContext": "CAWS CLI not available. Consider installing with: npm install -g @paths.design/caws-cli"
43
42
  }
44
43
  }'
45
44
  exit 0
46
45
  fi
47
46
 
48
- # Run CAWS quality gates in quiet mode for quick feedback
49
- if caws quality-gates --context=commit --quiet 2>/dev/null; then
50
- # Quality check passed - provide positive feedback
47
+ # Run quality gates via the unified pipeline
48
+ RESULT=$(caws gates run --context=edit --file "$FILE_PATH" --json --quiet 2>&1) || GATE_EXIT=$?
49
+
50
+ if [ -z "$RESULT" ]; then
51
+ # No output — gates command not available or errored
52
+ echo '{
53
+ "hookSpecificOutput": {
54
+ "hookEventName": "PostToolUse",
55
+ "additionalContext": "Quality gates did not produce output (exit '"${GATE_EXIT:-0}"'). Run '\''caws gates run'\'' for details."
56
+ }
57
+ }'
58
+ exit 0
59
+ fi
60
+
61
+ # Check if gates passed
62
+ PASSED=$(echo "$RESULT" | jq -r '.passed // true' 2>/dev/null)
63
+
64
+ if [ "$PASSED" = "true" ]; then
51
65
  echo '{
52
66
  "hookSpecificOutput": {
53
67
  "hookEventName": "PostToolUse",
@@ -55,13 +69,12 @@ if caws quality-gates --context=commit --quiet 2>/dev/null; then
55
69
  }
56
70
  }'
57
71
  else
58
- # Quality check failed - provide feedback to Claude
59
- # Run again to get violations summary
60
- VIOLATIONS=$(caws quality-gates --context=commit --json 2>/dev/null | jq -r '.violations[:3] | .[] | "- \(.gate): \(.message)"' 2>/dev/null || echo "Run 'caws quality-gates' for details")
72
+ # Extract top 3 gate failure messages
73
+ VIOLATIONS=$(echo "$RESULT" | jq -r '[.gates[] | select(.status == "fail") | "- \(.name): \(.messages[0] // "failed")"] | .[0:3] | .[]' 2>/dev/null || echo "Run 'caws gates run' for details")
61
74
 
62
75
  echo '{
63
76
  "decision": "block",
64
- "reason": "Quality gate violations detected. Please address the following issues before continuing:\n'"$VIOLATIONS"'\n\nRun '\''caws quality-gates'\'' for full details."
77
+ "reason": "Quality gate violations detected. Please address the following issues before continuing:\n'"$VIOLATIONS"'\n\nRun '\''caws gates run'\'' for full details."
65
78
  }'
66
79
  fi
67
80
 
@@ -123,27 +123,28 @@ if [[ ! -f "$SPEC_FILE" ]] && [[ -f "$SCOPE_FILE" ]]; then
123
123
  }
124
124
  " 2>&1)
125
125
 
126
+
127
+ if [[ "$LITE_CHECK" == error:* ]]; then
128
+ ERROR_MSG="${LITE_CHECK#error:}"
129
+ echo "BLOCKED: Scope check failed — cannot verify file is in scope" >&2
130
+ echo " Error: $ERROR_MSG" >&2
131
+ exit 2
132
+ fi
133
+
126
134
  if [[ "$LITE_CHECK" == banned:* ]]; then
127
135
  PATTERN="${LITE_CHECK#banned:}"
128
- echo '{
129
- "hookSpecificOutput": {
130
- "hookEventName": "PreToolUse",
131
- "permissionDecision": "ask",
132
- "permissionDecisionReason": "This file ('"$REL_PATH"') matches a banned pattern ('"$PATTERN"') in .caws/scope.json. Creating files with this pattern is blocked to prevent file sprawl."
133
- }
134
- }'
135
- exit 0
136
+ echo "BLOCKED: $REL_PATH matches banned pattern ($PATTERN) in .caws/scope.json"
137
+ echo " Scope allows: files not matching banned patterns"
138
+ echo " To modify scope, update bannedPatterns in .caws/scope.json"
139
+ exit 2
136
140
  fi
137
141
 
138
142
  if [[ "$LITE_CHECK" == "not_allowed" ]]; then
139
- echo '{
140
- "hookSpecificOutput": {
141
- "hookEventName": "PreToolUse",
142
- "permissionDecision": "ask",
143
- "permissionDecisionReason": "This file ('"$REL_PATH"') is outside the allowed directories in .caws/scope.json. Please confirm this edit is intentional."
144
- }
145
- }'
146
- exit 0
143
+ ALLOWED_DIRS=$(node -e "const s=JSON.parse(require('fs').readFileSync('$SCOPE_FILE','utf8')); console.log((s.allowedDirectories||[]).join(', '))" 2>/dev/null || echo "unknown")
144
+ echo "BLOCKED: $REL_PATH is outside allowed directories"
145
+ echo " Scope allows: $ALLOWED_DIRS"
146
+ echo " To modify scope, update allowedDirectories in .caws/scope.json"
147
+ exit 2
147
148
  fi
148
149
 
149
150
  # File is allowed - exit normally
@@ -270,29 +271,30 @@ if command -v node >/dev/null 2>&1; then
270
271
  }
271
272
  " 2>&1)
272
273
 
274
+
275
+ if [[ "$SCOPE_CHECK" == error:* ]]; then
276
+ ERROR_MSG="${SCOPE_CHECK#error:}"
277
+ echo "BLOCKED: Scope check failed — cannot verify file is in scope" >&2
278
+ echo " Error: $ERROR_MSG" >&2
279
+ echo " Fix the spec file or scope configuration before editing files" >&2
280
+ exit 2
281
+ fi
282
+
273
283
  if [[ "$SCOPE_CHECK" == out_of_scope:* ]]; then
274
284
  DETAIL="${SCOPE_CHECK#out_of_scope:}"
275
285
  SOURCE="${DETAIL%%:*}"
276
286
  PATTERN="${DETAIL#*:}"
277
- echo '{
278
- "hookSpecificOutput": {
279
- "hookEventName": "PreToolUse",
280
- "permissionDecision": "ask",
281
- "permissionDecisionReason": "This file ('"$REL_PATH"') is marked as out-of-scope in '"$SOURCE"' (pattern: '"$PATTERN"'). Editing it may cause scope creep. Please confirm this edit is intentional."
282
- }
283
- }'
284
- exit 0
287
+ echo "BLOCKED: $REL_PATH is excluded by scope.out in $SOURCE (pattern: $PATTERN)"
288
+ echo " Scope allows: files not matching scope.out patterns"
289
+ echo " To modify scope, update the spec's scope.out field"
290
+ exit 2
285
291
  fi
286
292
 
287
293
  if [[ "$SCOPE_CHECK" == "not_in_scope" ]]; then
288
- echo '{
289
- "hookSpecificOutput": {
290
- "hookEventName": "PreToolUse",
291
- "permissionDecision": "ask",
292
- "permissionDecisionReason": "This file ('"$REL_PATH"') is not in the defined scope of any active spec. Editing it may cause scope creep. Please confirm this edit is intentional."
293
- }
294
- }'
295
- exit 0
294
+ echo "BLOCKED: $REL_PATH is not in the defined scope.in of any active spec"
295
+ echo " Scope allows: files matching scope.in patterns in active specs"
296
+ echo " To modify scope, update the spec's scope.in field"
297
+ exit 2
296
298
  fi
297
299
  fi
298
300
 
@@ -40,7 +40,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
40
40
  WT_INFO=$(node -e "
41
41
  try {
42
42
  var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
43
- var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
43
+ var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
44
44
  if (active.length > 0) {
45
45
  var names = active.map(function(w) { return w.name + ' (' + w.branch + ')'; });
46
46
  console.log(active.length + ':' + names.join(', '));
@@ -58,7 +58,7 @@ if [ -f "$CAWS_ROOT/.caws/worktrees.json" ] && command -v node >/dev/null 2>&1;
58
58
  BASE_BRANCH=$(node -e "
59
59
  try {
60
60
  var reg = JSON.parse(require('fs').readFileSync('$CAWS_ROOT/.caws/worktrees.json', 'utf8'));
61
- var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
61
+ var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
62
62
  if (active.length > 0) console.log(active[0].baseBranch || '');
63
63
  else console.log('');
64
64
  } catch(e) { console.log(''); }
@@ -548,13 +548,86 @@ handle_pre_compact() {
548
548
  generate_session_output "$(resolve_transcript)"
549
549
  }
550
550
 
551
+ # ============================================================
552
+ # Agent registry heartbeat — register this agent with CAWS
553
+ # ============================================================
554
+ AGENTS_REGISTRY="${CWD}/.caws/agents.json"
555
+
556
+ heartbeat_agent() {
557
+ [ "$SESSION_ID" = "unknown" ] && return
558
+ [ ! -d "${CWD}/.caws" ] && return
559
+
560
+ local model_val
561
+ model_val=$(echo "$INPUT" | jq -r '.model // "unknown"' 2>/dev/null)
562
+
563
+ local registry
564
+ if [ -f "$AGENTS_REGISTRY" ]; then
565
+ registry=$(cat "$AGENTS_REGISTRY" 2>/dev/null || echo '{"version":1,"agents":{}}')
566
+ else
567
+ registry='{"version":1,"agents":{}}'
568
+ fi
569
+
570
+ registry=$(echo "$registry" | python3 -c "
571
+ import json, sys
572
+ from datetime import datetime, timedelta, timezone
573
+
574
+ TTL = timedelta(minutes=30)
575
+ now = datetime.now(timezone.utc)
576
+ sid = '$SESSION_ID'
577
+ model = '$model_val'
578
+
579
+ data = json.load(sys.stdin)
580
+ agents = data.get('agents', {})
581
+
582
+ pruned = {}
583
+ for k, entry in agents.items():
584
+ try:
585
+ last = datetime.fromisoformat(entry['lastSeen'].replace('Z', '+00:00'))
586
+ if now - last < TTL:
587
+ pruned[k] = entry
588
+ except (KeyError, ValueError):
589
+ pass
590
+
591
+ existing = pruned.get(sid, {})
592
+ pruned[sid] = {
593
+ 'sessionId': sid,
594
+ 'platform': 'claude-code',
595
+ 'model': model if model != 'unknown' else existing.get('model'),
596
+ 'specId': existing.get('specId'),
597
+ 'ttl': 1800000,
598
+ 'firstSeen': existing.get('firstSeen', now.strftime('%Y-%m-%dT%H:%M:%SZ')),
599
+ 'lastSeen': now.strftime('%Y-%m-%dT%H:%M:%SZ'),
600
+ }
601
+
602
+ data['agents'] = pruned
603
+ json.dump(data, sys.stdout, indent=2)
604
+ " 2>/dev/null)
605
+
606
+ [ -n "$registry" ] && echo "$registry" > "$AGENTS_REGISTRY"
607
+ }
608
+
609
+ remove_agent() {
610
+ [ "$SESSION_ID" = "unknown" ] && return
611
+ [ ! -f "$AGENTS_REGISTRY" ] && return
612
+
613
+ python3 -c "
614
+ import json
615
+ sid = '$SESSION_ID'
616
+ with open('$AGENTS_REGISTRY', 'r') as f:
617
+ data = json.load(f)
618
+ data.get('agents', {}).pop(sid, None)
619
+ with open('$AGENTS_REGISTRY', 'w') as f:
620
+ json.dump(data, f, indent=2)
621
+ " 2>/dev/null || true
622
+ }
623
+
551
624
  # ============================================================
552
625
  # DISPATCH
553
626
  # ============================================================
554
627
  case "$HOOK_EVENT" in
555
- SessionStart) handle_session_start ;;
556
- Stop) handle_stop ;;
557
- PreCompact) handle_pre_compact ;;
628
+ SessionStart) handle_session_start; heartbeat_agent ;;
629
+ Stop) handle_stop; remove_agent ;;
630
+ PreCompact) handle_pre_compact; heartbeat_agent ;;
558
631
  *) ;; # Other events: no-op
559
632
  esac
560
633
 
@@ -25,7 +25,7 @@ if [[ -f "$PROJECT_DIR/.caws/worktrees.json" ]] && command -v node >/dev/null 2>
25
25
  ACTIVE_INFO=$(node -e "
26
26
  try {
27
27
  var reg = JSON.parse(require('fs').readFileSync('$PROJECT_DIR/.caws/worktrees.json', 'utf8'));
28
- var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active'; });
28
+ var active = Object.values(reg.worktrees || {}).filter(function(w) { return w.status === 'active' || w.status === 'fresh' || w.status === 'merged'; });
29
29
  if (active.length > 0) {
30
30
  console.log(active.length + ':' + active.map(function(w) { return w.name; }).join(', '));
31
31
  } else {