specweave 0.30.19 → 0.32.2

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 (242) hide show
  1. package/CLAUDE.md +176 -2
  2. package/README.md +22 -0
  3. package/bin/specweave.js +18 -1
  4. package/dist/src/cli/commands/cache.d.ts +17 -0
  5. package/dist/src/cli/commands/cache.d.ts.map +1 -0
  6. package/dist/src/cli/commands/cache.js +126 -0
  7. package/dist/src/cli/commands/cache.js.map +1 -0
  8. package/dist/src/cli/commands/init.js +1 -1
  9. package/dist/src/cli/commands/init.js.map +1 -1
  10. package/dist/src/cli/commands/plan/increment-detector.js +2 -2
  11. package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
  12. package/dist/src/cli/commands/sync-spec-commits.js +1 -1
  13. package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
  14. package/dist/src/cli/commands/sync-specs.js +2 -2
  15. package/dist/src/cli/commands/sync-specs.js.map +1 -1
  16. package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
  17. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  18. package/dist/src/cli/workers/living-docs-worker.js +66 -1
  19. package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
  20. package/dist/src/config/types.d.ts +203 -1208
  21. package/dist/src/config/types.d.ts.map +1 -1
  22. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
  23. package/dist/src/core/discrepancy/increment-generator.js +5 -2
  24. package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
  25. package/dist/src/core/external-tools/external-items-counter.d.ts +62 -0
  26. package/dist/src/core/external-tools/external-items-counter.d.ts.map +1 -0
  27. package/dist/src/core/external-tools/external-items-counter.js +206 -0
  28. package/dist/src/core/external-tools/external-items-counter.js.map +1 -0
  29. package/dist/src/core/external-tools/external-items-display.d.ts +39 -0
  30. package/dist/src/core/external-tools/external-items-display.d.ts.map +1 -0
  31. package/dist/src/core/external-tools/external-items-display.js +185 -0
  32. package/dist/src/core/external-tools/external-items-display.js.map +1 -0
  33. package/dist/src/core/external-tools/index.d.ts +8 -0
  34. package/dist/src/core/external-tools/index.d.ts.map +1 -0
  35. package/dist/src/core/external-tools/index.js +8 -0
  36. package/dist/src/core/external-tools/index.js.map +1 -0
  37. package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts +39 -0
  38. package/dist/src/core/external-tools/providers/ado-items-adapter.d.ts.map +1 -0
  39. package/dist/src/core/external-tools/providers/ado-items-adapter.js +188 -0
  40. package/dist/src/core/external-tools/providers/ado-items-adapter.js.map +1 -0
  41. package/dist/src/core/external-tools/providers/github-items-adapter.d.ts +38 -0
  42. package/dist/src/core/external-tools/providers/github-items-adapter.d.ts.map +1 -0
  43. package/dist/src/core/external-tools/providers/github-items-adapter.js +136 -0
  44. package/dist/src/core/external-tools/providers/github-items-adapter.js.map +1 -0
  45. package/dist/src/core/external-tools/providers/index.d.ts +7 -0
  46. package/dist/src/core/external-tools/providers/index.d.ts.map +1 -0
  47. package/dist/src/core/external-tools/providers/index.js +7 -0
  48. package/dist/src/core/external-tools/providers/index.js.map +1 -0
  49. package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts +42 -0
  50. package/dist/src/core/external-tools/providers/jira-items-adapter.d.ts.map +1 -0
  51. package/dist/src/core/external-tools/providers/jira-items-adapter.js +153 -0
  52. package/dist/src/core/external-tools/providers/jira-items-adapter.js.map +1 -0
  53. package/dist/src/core/external-tools/types.d.ts +78 -0
  54. package/dist/src/core/external-tools/types.d.ts.map +1 -0
  55. package/dist/src/core/external-tools/types.js +19 -0
  56. package/dist/src/core/external-tools/types.js.map +1 -0
  57. package/dist/src/core/increment/duplicate-detector.js +2 -2
  58. package/dist/src/core/increment/duplicate-detector.js.map +1 -1
  59. package/dist/src/core/increment/increment-archiver.d.ts +24 -0
  60. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  61. package/dist/src/core/increment/increment-archiver.js +59 -2
  62. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  63. package/dist/src/core/increment/increment-status.js +2 -2
  64. package/dist/src/core/increment/increment-status.js.map +1 -1
  65. package/dist/src/core/increment/increment-utils.d.ts +98 -37
  66. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  67. package/dist/src/core/increment/increment-utils.js +119 -68
  68. package/dist/src/core/increment/increment-utils.js.map +1 -1
  69. package/dist/src/core/increment/metadata-validator.js +1 -1
  70. package/dist/src/core/increment/metadata-validator.js.map +1 -1
  71. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  72. package/dist/src/core/increment/status-change-sync-trigger.js +4 -0
  73. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  74. package/dist/src/core/living-docs/feature-id-manager.js +1 -1
  75. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  76. package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
  77. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  78. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
  79. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
  80. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +247 -0
  81. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
  82. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
  83. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
  84. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
  85. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
  86. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
  87. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
  88. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
  89. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
  90. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
  91. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
  92. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
  93. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
  94. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
  95. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
  96. package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
  97. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
  98. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +18 -0
  99. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
  100. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +154 -0
  101. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
  102. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
  103. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
  104. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
  105. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
  106. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +146 -0
  107. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
  108. package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
  109. package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
  110. package/dist/src/core/living-docs/living-docs-sync.d.ts +5 -0
  111. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  112. package/dist/src/core/living-docs/living-docs-sync.js +36 -2
  113. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  114. package/dist/src/core/llm/providers/azure-openai-provider.d.ts.map +1 -1
  115. package/dist/src/core/llm/providers/azure-openai-provider.js +1 -0
  116. package/dist/src/core/llm/providers/azure-openai-provider.js.map +1 -1
  117. package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
  118. package/dist/src/core/llm/providers/bedrock-provider.js +2 -0
  119. package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
  120. package/dist/src/core/llm/providers/openai-provider.d.ts.map +1 -1
  121. package/dist/src/core/llm/providers/openai-provider.js +1 -0
  122. package/dist/src/core/llm/providers/openai-provider.js.map +1 -1
  123. package/dist/src/core/llm/providers/vertex-ai-provider.d.ts.map +1 -1
  124. package/dist/src/core/llm/providers/vertex-ai-provider.js +1 -0
  125. package/dist/src/core/llm/providers/vertex-ai-provider.js.map +1 -1
  126. package/dist/src/core/sync/spec-increment-mapper.js +3 -3
  127. package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
  128. package/dist/src/importers/item-converter.d.ts +25 -0
  129. package/dist/src/importers/item-converter.d.ts.map +1 -1
  130. package/dist/src/importers/item-converter.js +135 -5
  131. package/dist/src/importers/item-converter.js.map +1 -1
  132. package/dist/src/init/architecture/types.d.ts +33 -140
  133. package/dist/src/init/architecture/types.d.ts.map +1 -1
  134. package/dist/src/init/compliance/types.d.ts +30 -27
  135. package/dist/src/init/compliance/types.d.ts.map +1 -1
  136. package/dist/src/init/repo/types.d.ts +11 -34
  137. package/dist/src/init/repo/types.d.ts.map +1 -1
  138. package/dist/src/init/research/src/config/types.d.ts +15 -82
  139. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  140. package/dist/src/init/research/types.d.ts +38 -93
  141. package/dist/src/init/research/types.d.ts.map +1 -1
  142. package/dist/src/init/team/types.d.ts +4 -42
  143. package/dist/src/init/team/types.d.ts.map +1 -1
  144. package/dist/src/types/dashboard-cache.d.ts +181 -0
  145. package/dist/src/types/dashboard-cache.d.ts.map +1 -0
  146. package/dist/src/types/dashboard-cache.js +65 -0
  147. package/dist/src/types/dashboard-cache.js.map +1 -0
  148. package/dist/src/utils/docs-validator.d.ts +131 -0
  149. package/dist/src/utils/docs-validator.d.ts.map +1 -0
  150. package/dist/src/utils/docs-validator.js +529 -0
  151. package/dist/src/utils/docs-validator.js.map +1 -0
  152. package/dist/src/utils/feature-id-collision.js +1 -1
  153. package/dist/src/utils/feature-id-collision.js.map +1 -1
  154. package/dist/src/utils/html-to-mdx.d.ts +1 -0
  155. package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
  156. package/dist/src/utils/html-to-mdx.js +43 -5
  157. package/dist/src/utils/html-to-mdx.js.map +1 -1
  158. package/package.json +1 -5
  159. package/plugins/specweave/agents/pm/AGENT.md +10 -7
  160. package/plugins/specweave/commands/specweave-archive-features.md +5 -7
  161. package/plugins/specweave/commands/specweave-archive.md +2 -1
  162. package/plugins/specweave/commands/specweave-do.md +35 -1
  163. package/plugins/specweave/commands/specweave-done.md +96 -0
  164. package/plugins/specweave/commands/specweave-external.md +150 -0
  165. package/plugins/specweave/commands/specweave-import-external.md +45 -18
  166. package/plugins/specweave/commands/specweave-increment.md +331 -33
  167. package/plugins/specweave/commands/specweave-jobs.md +2 -2
  168. package/plugins/specweave/commands/specweave-progress.md +4 -4
  169. package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
  170. package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
  171. package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
  172. package/plugins/specweave/commands/specweave-validate-features.md +13 -8
  173. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  174. package/plugins/specweave/hooks/hooks.json +33 -4
  175. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  176. package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
  177. package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
  178. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  179. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  180. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  181. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  182. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  183. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  184. package/plugins/specweave/hooks/post-task-completion.sh +4 -23
  185. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  186. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
  187. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  188. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  189. package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
  190. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  191. package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
  192. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  193. package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
  194. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
  195. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  196. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  197. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  198. package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
  199. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  200. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
  201. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
  202. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
  203. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
  204. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
  205. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
  206. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
  207. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
  208. package/plugins/specweave/scripts/README.md +166 -0
  209. package/plugins/specweave/scripts/cleanup-state.sh +142 -0
  210. package/plugins/specweave/scripts/force-kill.sh +142 -0
  211. package/plugins/specweave/scripts/jobs.js +171 -0
  212. package/plugins/specweave/scripts/progress.js +170 -0
  213. package/plugins/specweave/scripts/read-costs.sh +132 -0
  214. package/plugins/specweave/scripts/read-jobs.sh +324 -0
  215. package/plugins/specweave/scripts/read-progress.sh +185 -0
  216. package/plugins/specweave/scripts/read-status.sh +146 -0
  217. package/plugins/specweave/scripts/read-workflow.sh +173 -0
  218. package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
  219. package/plugins/specweave/scripts/session-watchdog.sh +192 -0
  220. package/plugins/specweave/scripts/status.js +154 -0
  221. package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
  222. package/plugins/specweave/skills/increment-planner/SKILL.md +333 -24
  223. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -9
  224. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -2
  225. package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
  226. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  227. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  228. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  229. package/plugins/specweave-docs/commands/build.md +32 -4
  230. package/plugins/specweave-docs/commands/preview.md +43 -1
  231. package/plugins/specweave-docs/commands/validate.md +250 -0
  232. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -626
  233. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  234. package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
  235. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  236. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  237. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -939
  238. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  239. package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
  240. package/plugins/specweave/hooks/post-write-spec.sh +0 -267
  241. package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
  242. package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
@@ -1,11 +1,14 @@
1
1
  ---
2
2
  name: specweave:validate-features
3
- description: Validate feature folder consistency between _features/ and project folders. Detects orphaned features and auto-repairs discrepancies. Use when features appear in _features but not in project folders.
3
+ description: Validate feature folder consistency across project folders. Detects orphaned features, missing FEATURE.md, and auto-repairs discrepancies. Use for periodic health checks of living docs structure.
4
4
  ---
5
5
 
6
6
  # Validate Feature Folder Consistency
7
7
 
8
- Validates that features in `_features/` have corresponding project folders (e.g., `specweave/FS-XXX/`).
8
+ Validates that feature folders in project directories (e.g., `specweave/FS-XXX/`) have proper structure.
9
+
10
+ **Note (v5.0.0+)**: The `_features/` folder is OBSOLETE. Features live in `{project}/FS-XXX/`.
11
+ If you find features in `_features/`, they should be migrated to the correct project folder.
9
12
 
10
13
  ---
11
14
 
@@ -120,9 +123,9 @@ Manual intervention required for:
120
123
  {list orphaned features}
121
124
 
122
125
  Options:
123
- 1. Delete orphaned _features folder if no longer needed
126
+ 1. Delete orphaned folder if no longer needed
124
127
  2. Re-sync from increment if increment still exists
125
- 3. Create project folder manually
128
+ 3. Move to correct project folder manually
126
129
  {/if}
127
130
  ```
128
131
 
@@ -147,8 +150,8 @@ Discrepancies found: 1
147
150
  ⚠️ DISCREPANCIES FOUND
148
151
 
149
152
  Feature: FS-062
150
- Type: missing_project_folder
151
- Description: Feature FS-062 exists in _features/ but not in any project folder
153
+ Type: missing_feature_md
154
+ Description: Feature FS-062 folder exists but missing FEATURE.md
152
155
  Auto-repairable: Yes
153
156
  Linked increment: 0062-test-living-docs-auto-sync (not found)
154
157
 
@@ -184,15 +187,17 @@ Discrepancies found: 1
184
187
  ## WHEN TO USE
185
188
 
186
189
  **Use this command when**:
187
- - Features appear in `_features/` but not in project folders
190
+ - Feature folders are missing FEATURE.md or us-*.md files
188
191
  - After interrupted/failed sync operations
189
192
  - After manual cleanup of increments
190
193
  - Periodic health check of living docs structure
194
+ - Legacy migration from `_features/` to `{project}/` folders
191
195
 
192
196
  **Root cause of discrepancies**:
193
- 1. Sync interrupted between creating _features and project folders
197
+ 1. Sync interrupted during feature creation
194
198
  2. Increment deleted without cleaning up living docs
195
199
  3. Manual editing of living docs structure
200
+ 4. Legacy `_features/` folders not yet migrated (v5.0.0+)
196
201
 
197
202
  ---
198
203
 
@@ -0,0 +1,79 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Docs-Changed Hook
4
+ # Runs after file changes are detected
5
+ # Detects if documentation was changed during implementation
6
+ # Triggers review workflow if needed
7
+
8
+ set -e
9
+
10
+ # Find project root by searching upward for .specweave/ directory
11
+ # Works regardless of where hook is installed (source or .claude/hooks/)
12
+ find_project_root() {
13
+ local dir="$1"
14
+ while [ "$dir" != "/" ]; do
15
+ if [ -d "$dir/.specweave" ]; then
16
+ echo "$dir"
17
+ return 0
18
+ fi
19
+ dir="$(dirname "$dir")"
20
+ done
21
+ # Fallback: try current directory
22
+ if [ -d "$(pwd)/.specweave" ]; then
23
+ pwd
24
+ else
25
+ echo "$(pwd)"
26
+ fi
27
+ }
28
+
29
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
30
+ cd "$PROJECT_ROOT"
31
+
32
+ # Colors
33
+ RED='\033[0;31m'
34
+ YELLOW='\033[1;33m'
35
+ NC='\033[0m'
36
+
37
+ # Get changed files (git)
38
+ if ! git rev-parse --git-dir > /dev/null 2>&1; then
39
+ # Not a git repository, skip
40
+ exit 0
41
+ fi
42
+
43
+ CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null || echo "")
44
+
45
+ if [ -z "$CHANGED_FILES" ]; then
46
+ # No changes
47
+ exit 0
48
+ fi
49
+
50
+ # Check if any documentation files changed
51
+ DOC_CHANGES=$(echo "$CHANGED_FILES" | grep -E '\.specweave/(docs|increments/.*/.*\.md)' || true)
52
+
53
+ if [ -n "$DOC_CHANGES" ]; then
54
+ echo -e "${RED}⚠️ Documentation changed during implementation${NC}"
55
+ echo ""
56
+ echo "📋 Files changed:"
57
+ echo "$DOC_CHANGES" | sed 's/^/ /'
58
+ echo ""
59
+ echo -e "${YELLOW}🔔 Recommended actions:${NC}"
60
+ echo " 1. Review documentation changes"
61
+ echo " 2. Update tasks.md if architecture changed"
62
+ echo " 3. Type /review-docs to see full impact"
63
+ echo ""
64
+
65
+ # Play notification sound
66
+ case "$(uname -s)" in
67
+ Darwin)
68
+ afplay /System/Library/Sounds/Ping.aiff 2>/dev/null &
69
+ ;;
70
+ Linux)
71
+ paplay /usr/share/sounds/freedesktop/stereo/dialog-warning.oga 2>/dev/null || true
72
+ ;;
73
+ esac
74
+
75
+ # Log to hooks log
76
+ LOGS_DIR=".specweave/logs"
77
+ mkdir -p "$LOGS_DIR"
78
+ echo "[$(date)] Documentation changed: $DOC_CHANGES" >> "$LOGS_DIR/hooks.log"
79
+ fi
@@ -5,19 +5,38 @@
5
5
  "hooks": [
6
6
  {
7
7
  "type": "command",
8
- "command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\"; [ -f \"$F\" ] && node \"$F\" session-start || printf \"{\\\"continue\\\":true}\"' 2>/dev/null || printf '{\"continue\":true}'"
8
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/dispatchers/session-start.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"continue\\\":true}\")'"
9
+ }
10
+ ]
11
+ }
12
+ ],
13
+ "UserPromptSubmit": [
14
+ {
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"approve\\\"}\")'"
9
19
  }
10
20
  ]
11
21
  }
12
22
  ],
13
23
  "PreToolUse": [
24
+ {
25
+ "matcher": "Bash",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/bash-file-guard.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"allow\\\"}\")'"
30
+ }
31
+ ]
32
+ },
14
33
  {
15
34
  "matcher": "Edit|Write",
16
35
  "matcher_content": "metadata\\.json.*completed",
17
36
  "hooks": [
18
37
  {
19
38
  "type": "command",
20
- "command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\"; [ -f \"$F\" ] && node \"$F\" completion-guard || printf \"{\\\"continue\\\":true}\"' 2>/dev/null || printf '{\"continue\":true}'"
39
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/completion-guard.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"allow\\\"}\")'"
21
40
  }
22
41
  ]
23
42
  },
@@ -27,7 +46,17 @@
27
46
  "hooks": [
28
47
  {
29
48
  "type": "command",
30
- "command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/spec-project-validator.sh\"; [ -f \"$F\" ] && \"$F\" || printf \"{\\\"decision\\\":\\\"allow\\\"}\"' 2>/dev/null || printf '{\"decision\":\"allow\"}'"
49
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/spec-project-validator.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"allow\\\"}\")'"
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "matcher": "Write|Edit",
55
+ "matcher_content": "\\.specweave/docs/internal/specs/_features/",
56
+ "hooks": [
57
+ {
58
+ "type": "command",
59
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/guards/features-folder-guard.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"decision\\\":\\\"allow\\\"}\")'"
31
60
  }
32
61
  ]
33
62
  }
@@ -39,7 +68,7 @@
39
68
  "hooks": [
40
69
  {
41
70
  "type": "command",
42
- "command": "bash -c 'F=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/dispatcher.mjs\"; [ -f \"$F\" ] && node \"$F\" post-tool-use || printf \"{\\\"continue\\\":true}\"' 2>/dev/null || printf '{\"continue\":true}'"
71
+ "command": "bash -c 'W=\"${CLAUDE_PLUGIN_ROOT}/hooks/universal/fail-fast-wrapper.sh\"; S=\"${CLAUDE_PLUGIN_ROOT}/hooks/v2/dispatchers/post-tool-use.sh\"; [[ -x \"$W\" ]] && exec \"$W\" \"$S\" || (cat >/dev/null && printf \"{\\\"continue\\\":true}\")'"
43
72
  }
44
73
  ]
45
74
  }
@@ -0,0 +1,75 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Human-Input-Required Hook
4
+ # Runs when Claude needs clarification or approval
5
+ #
6
+ # Actions:
7
+ # 1. Play notification sound (Ping.aiff)
8
+ # 2. Log the question/requirement
9
+ # 3. Record in current increment's work log (if applicable)
10
+
11
+ set -e
12
+
13
+ # Find project root by searching upward for .specweave/ directory
14
+ # Works regardless of where hook is installed (source or .claude/hooks/)
15
+ find_project_root() {
16
+ local dir="$1"
17
+ while [ "$dir" != "/" ]; do
18
+ if [ -d "$dir/.specweave" ]; then
19
+ echo "$dir"
20
+ return 0
21
+ fi
22
+ dir="$(dirname "$dir")"
23
+ done
24
+ # Fallback: try current directory
25
+ if [ -d "$(pwd)/.specweave" ]; then
26
+ pwd
27
+ else
28
+ echo "$(pwd)"
29
+ fi
30
+ }
31
+
32
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
33
+ cd "$PROJECT_ROOT"
34
+
35
+ # Get question/requirement (passed as argument or default)
36
+ QUESTION="${1:-User input required}"
37
+
38
+ echo "❓ Human input required"
39
+
40
+ # 1. Play notification sound (cross-platform)
41
+ play_sound() {
42
+ case "$(uname -s)" in
43
+ Darwin)
44
+ afplay /System/Library/Sounds/Ping.aiff 2>/dev/null &
45
+ ;;
46
+ Linux)
47
+ paplay /usr/share/sounds/freedesktop/stereo/dialog-question.oga 2>/dev/null || \
48
+ aplay /usr/share/sounds/alsa/Side_Left.wav 2>/dev/null || true
49
+ ;;
50
+ MINGW*|MSYS*|CYGWIN*)
51
+ powershell -c "(New-Object Media.SoundPlayer 'C:\Windows\Media\notify.wav').PlaySync();" 2>/dev/null || true
52
+ ;;
53
+ esac
54
+ }
55
+
56
+ play_sound &
57
+
58
+ # 2. Log to main hooks log
59
+ LOGS_DIR=".specweave/logs"
60
+ mkdir -p "$LOGS_DIR"
61
+ echo "[$(date)] Human input required: $QUESTION" >> "$LOGS_DIR/hooks.log"
62
+
63
+ # 3. Log to current work context (if exists)
64
+ CURRENT_WORK=$(find .specweave/work -maxdepth 1 -type d -name "current-*" | head -1 || true)
65
+
66
+ if [ -n "$CURRENT_WORK" ] && [ -d "$CURRENT_WORK" ]; then
67
+ echo "" >> "$CURRENT_WORK/notes.md"
68
+ echo "## Input Required ($(date +%Y-%m-%d\ %H:%M))" >> "$CURRENT_WORK/notes.md"
69
+ echo "" >> "$CURRENT_WORK/notes.md"
70
+ echo "$QUESTION" >> "$CURRENT_WORK/notes.md"
71
+ echo "" >> "$CURRENT_WORK/notes.md"
72
+ echo "📝 Logged to $CURRENT_WORK/notes.md"
73
+ fi
74
+
75
+ echo "✅ Hook complete"
@@ -0,0 +1,375 @@
1
+ #!/bin/bash
2
+ # common-setup.sh - Unified Hook Library for Crash Prevention
3
+ #
4
+ # This library consolidates ALL common patterns from 19+ hooks into
5
+ # a single source of truth. Extracted from ADRs: 0060, 0068, 0073,
6
+ # 0128, 0130, 0157, 0189.
7
+ #
8
+ # USAGE:
9
+ # source "${CLAUDE_PLUGIN_ROOT}/hooks/lib/common-setup.sh"
10
+ # setup_hook_environment || exit 0
11
+ #
12
+ # v0.33.0 - Consolidated from 7 ADRs (2,500+ lines → 200 lines)
13
+
14
+ set +e # NEVER use set -e in hooks (ADR-0157 Rule 2)
15
+
16
+ # ============================================================================
17
+ # CONFIGURATION
18
+ # ============================================================================
19
+
20
+ export HOOK_VERSION="0.33.0"
21
+ export HOOK_TIMEOUT="${HOOK_TIMEOUT:-5}"
22
+ export HOOK_DEBUG="${HOOK_DEBUG:-0}"
23
+
24
+ # State directories (lazy-created)
25
+ _STATE_DIR=""
26
+ _CACHE_DIR=""
27
+ _LOG_DIR=""
28
+
29
+ # ============================================================================
30
+ # 1. PROJECT ROOT DETECTION (ADR-0068, ADR-0128)
31
+ # ============================================================================
32
+
33
+ find_project_root() {
34
+ local dir="${1:-$(pwd)}"
35
+ while [[ "$dir" != "/" ]]; do
36
+ if [[ -d "$dir/.specweave" ]]; then
37
+ echo "$dir"
38
+ return 0
39
+ fi
40
+ dir=$(dirname "$dir")
41
+ done
42
+ return 1
43
+ }
44
+
45
+ # ============================================================================
46
+ # 2. EMERGENCY KILL SWITCH (ADR-0068 Layer 1, ADR-0157 Rule 4)
47
+ # ============================================================================
48
+
49
+ check_kill_switch() {
50
+ if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
51
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Kill switch active" >&2
52
+ return 1
53
+ fi
54
+ return 0
55
+ }
56
+
57
+ # ============================================================================
58
+ # 3. CIRCUIT BREAKER (ADR-0068 Layer 2)
59
+ # ============================================================================
60
+
61
+ # Circuit breaker state (3 failures = trip)
62
+ _CIRCUIT_BREAKER_FILE=""
63
+ _CIRCUIT_BREAKER_THRESHOLD=3
64
+
65
+ init_circuit_breaker() {
66
+ local project_root="$1"
67
+ _CIRCUIT_BREAKER_FILE="${project_root}/.specweave/state/.hook-circuit-breaker"
68
+ mkdir -p "$(dirname "$_CIRCUIT_BREAKER_FILE")" 2>/dev/null
69
+ }
70
+
71
+ check_circuit_breaker() {
72
+ [[ -z "$_CIRCUIT_BREAKER_FILE" ]] && return 0
73
+
74
+ if [[ -f "$_CIRCUIT_BREAKER_FILE" ]]; then
75
+ local failures
76
+ failures=$(cat "$_CIRCUIT_BREAKER_FILE" 2>/dev/null || echo "0")
77
+ if [[ "$failures" -ge "$_CIRCUIT_BREAKER_THRESHOLD" ]]; then
78
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Circuit breaker OPEN ($failures failures)" >&2
79
+ return 1
80
+ fi
81
+ fi
82
+ return 0
83
+ }
84
+
85
+ record_circuit_breaker_failure() {
86
+ [[ -z "$_CIRCUIT_BREAKER_FILE" ]] && return
87
+
88
+ local failures
89
+ failures=$(cat "$_CIRCUIT_BREAKER_FILE" 2>/dev/null || echo "0")
90
+ echo "$((failures + 1))" > "$_CIRCUIT_BREAKER_FILE"
91
+ }
92
+
93
+ reset_circuit_breaker() {
94
+ [[ -n "$_CIRCUIT_BREAKER_FILE" ]] && rm -f "$_CIRCUIT_BREAKER_FILE"
95
+ }
96
+
97
+ # ============================================================================
98
+ # 4. FILE-BASED RECURSION GUARD (ADR-0073 - replaces failed env var approach)
99
+ # ============================================================================
100
+
101
+ _RECURSION_GUARD_FILE=""
102
+
103
+ init_recursion_guard() {
104
+ local project_root="$1"
105
+ _RECURSION_GUARD_FILE="${project_root}/.specweave/state/.hook-recursion-guard"
106
+ }
107
+
108
+ check_recursion_guard() {
109
+ [[ -z "$_RECURSION_GUARD_FILE" ]] && return 0
110
+
111
+ if [[ -f "$_RECURSION_GUARD_FILE" ]]; then
112
+ # Check if guard is stale (>30s old)
113
+ local guard_age
114
+ if [[ "$(uname)" == "Darwin" ]]; then
115
+ guard_age=$(( $(date +%s) - $(stat -f %m "$_RECURSION_GUARD_FILE" 2>/dev/null || echo "0") ))
116
+ else
117
+ guard_age=$(( $(date +%s) - $(stat -c %Y "$_RECURSION_GUARD_FILE" 2>/dev/null || echo "0") ))
118
+ fi
119
+
120
+ if [[ "$guard_age" -lt 30 ]]; then
121
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Recursion guard active (${guard_age}s old)" >&2
122
+ return 1
123
+ else
124
+ # Stale guard - clean it up
125
+ rm -f "$_RECURSION_GUARD_FILE"
126
+ fi
127
+ fi
128
+ return 0
129
+ }
130
+
131
+ acquire_recursion_guard() {
132
+ [[ -z "$_RECURSION_GUARD_FILE" ]] && return 1
133
+
134
+ touch "$_RECURSION_GUARD_FILE"
135
+ # Auto-cleanup on exit
136
+ trap 'rm -f "$_RECURSION_GUARD_FILE" 2>/dev/null' EXIT
137
+ return 0
138
+ }
139
+
140
+ release_recursion_guard() {
141
+ [[ -n "$_RECURSION_GUARD_FILE" ]] && rm -f "$_RECURSION_GUARD_FILE" 2>/dev/null
142
+ }
143
+
144
+ # ============================================================================
145
+ # 5. LOCK FILE ACQUISITION (ADR-0068 Layer 3)
146
+ # ============================================================================
147
+
148
+ _LOCK_FILE=""
149
+ _LOCK_TIMEOUT=5
150
+
151
+ acquire_hook_lock() {
152
+ local lock_name="${1:-default}"
153
+ local project_root="${2:-$(find_project_root)}"
154
+
155
+ _LOCK_FILE="${project_root}/.specweave/state/.hook-${lock_name}.lock"
156
+ mkdir -p "$(dirname "$_LOCK_FILE")" 2>/dev/null
157
+
158
+ local attempts=0
159
+ while [[ $attempts -lt $((_LOCK_TIMEOUT * 5)) ]]; do
160
+ if mkdir "$_LOCK_FILE" 2>/dev/null; then
161
+ trap 'rmdir "$_LOCK_FILE" 2>/dev/null || true' EXIT
162
+ return 0
163
+ fi
164
+
165
+ # Check for stale lock (>60s old)
166
+ if [[ -d "$_LOCK_FILE" ]]; then
167
+ local lock_age
168
+ if [[ "$(uname)" == "Darwin" ]]; then
169
+ lock_age=$(( $(date +%s) - $(stat -f %m "$_LOCK_FILE" 2>/dev/null || echo "0") ))
170
+ else
171
+ lock_age=$(( $(date +%s) - $(stat -c %Y "$_LOCK_FILE" 2>/dev/null || echo "0") ))
172
+ fi
173
+
174
+ if [[ "$lock_age" -gt 60 ]]; then
175
+ rmdir "$_LOCK_FILE" 2>/dev/null
176
+ continue
177
+ fi
178
+ fi
179
+
180
+ sleep 0.2
181
+ attempts=$((attempts + 1))
182
+ done
183
+
184
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Lock acquisition timeout: $lock_name" >&2
185
+ return 1
186
+ }
187
+
188
+ release_hook_lock() {
189
+ [[ -n "$_LOCK_FILE" ]] && rmdir "$_LOCK_FILE" 2>/dev/null
190
+ _LOCK_FILE=""
191
+ }
192
+
193
+ # ============================================================================
194
+ # 6. DEBOUNCING (ADR-0060 Tier 1, ADR-0130)
195
+ # ============================================================================
196
+
197
+ _DEBOUNCE_FILE=""
198
+ _DEBOUNCE_WINDOW=5 # seconds
199
+
200
+ check_debounce() {
201
+ local debounce_key="${1:-default}"
202
+ local project_root="${2:-$(find_project_root)}"
203
+
204
+ _DEBOUNCE_FILE="${project_root}/.specweave/state/.last-hook-${debounce_key}"
205
+
206
+ if [[ -f "$_DEBOUNCE_FILE" ]]; then
207
+ local last_run
208
+ last_run=$(cat "$_DEBOUNCE_FILE" 2>/dev/null || echo "0")
209
+ local now
210
+ now=$(date +%s)
211
+
212
+ if [[ $((now - last_run)) -lt $_DEBOUNCE_WINDOW ]]; then
213
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Debounced: $debounce_key" >&2
214
+ return 1
215
+ fi
216
+ fi
217
+ return 0
218
+ }
219
+
220
+ record_debounce() {
221
+ [[ -n "$_DEBOUNCE_FILE" ]] && date +%s > "$_DEBOUNCE_FILE"
222
+ }
223
+
224
+ # ============================================================================
225
+ # 7. BULK OPERATION DETECTION (ADR-0130)
226
+ # ============================================================================
227
+
228
+ _BULK_COUNTER_FILE=""
229
+ _BULK_THRESHOLD=5 # 5+ ops in 10s = bulk mode
230
+ _BULK_WINDOW=10
231
+
232
+ is_bulk_operation() {
233
+ local project_root="${1:-$(find_project_root)}"
234
+
235
+ _BULK_COUNTER_FILE="${project_root}/.specweave/state/.bulk-op-counter"
236
+ mkdir -p "$(dirname "$_BULK_COUNTER_FILE")" 2>/dev/null
237
+
238
+ local now
239
+ now=$(date +%s)
240
+
241
+ # Read current count and timestamp
242
+ local count=0
243
+ local start_time=$now
244
+
245
+ if [[ -f "$_BULK_COUNTER_FILE" ]]; then
246
+ read -r count start_time < "$_BULK_COUNTER_FILE" 2>/dev/null || true
247
+
248
+ # Reset if window expired
249
+ if [[ $((now - start_time)) -gt $_BULK_WINDOW ]]; then
250
+ count=0
251
+ start_time=$now
252
+ fi
253
+ fi
254
+
255
+ # Increment and save
256
+ count=$((count + 1))
257
+ echo "$count $start_time" > "$_BULK_COUNTER_FILE"
258
+
259
+ if [[ $count -ge $_BULK_THRESHOLD ]]; then
260
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Bulk operation detected ($count ops in window)" >&2
261
+ return 0 # IS bulk operation
262
+ fi
263
+ return 1 # NOT bulk operation
264
+ }
265
+
266
+ # ============================================================================
267
+ # 8. LOG ROTATION (ADR-0060)
268
+ # ============================================================================
269
+
270
+ _LOG_FILE=""
271
+ _LOG_MAX_SIZE=1048576 # 1MB
272
+
273
+ init_log() {
274
+ local log_name="${1:-hooks}"
275
+ local project_root="${2:-$(find_project_root)}"
276
+
277
+ _LOG_DIR="${project_root}/.specweave/logs"
278
+ mkdir -p "$_LOG_DIR" 2>/dev/null
279
+ _LOG_FILE="${_LOG_DIR}/${log_name}.log"
280
+
281
+ # Rotate if too large
282
+ if [[ -f "$_LOG_FILE" ]]; then
283
+ local size
284
+ size=$(stat -f%z "$_LOG_FILE" 2>/dev/null || stat -c%s "$_LOG_FILE" 2>/dev/null || echo "0")
285
+ if [[ "$size" -gt "$_LOG_MAX_SIZE" ]]; then
286
+ mv "$_LOG_FILE" "${_LOG_FILE}.1" 2>/dev/null
287
+ fi
288
+ fi
289
+ }
290
+
291
+ log_hook() {
292
+ local level="$1"
293
+ shift
294
+ local msg="$*"
295
+
296
+ [[ -z "$_LOG_FILE" ]] && return
297
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [$level] $msg" >> "$_LOG_FILE"
298
+ }
299
+
300
+ # ============================================================================
301
+ # 9. UNIFIED SETUP (ALL CHECKS IN ONE CALL)
302
+ # ============================================================================
303
+
304
+ setup_hook_environment() {
305
+ local hook_name="${1:-unnamed}"
306
+ local require_lock="${2:-false}"
307
+ local enable_recursion_guard="${3:-false}"
308
+
309
+ # Find project root
310
+ PROJECT_ROOT=$(find_project_root)
311
+ if [[ -z "$PROJECT_ROOT" ]]; then
312
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Not in SpecWeave project" >&2
313
+ return 1
314
+ fi
315
+ export PROJECT_ROOT
316
+
317
+ # Initialize state directory
318
+ _STATE_DIR="${PROJECT_ROOT}/.specweave/state"
319
+ mkdir -p "$_STATE_DIR" 2>/dev/null
320
+
321
+ # Layer 1: Kill switch
322
+ check_kill_switch || return 1
323
+
324
+ # Layer 2: Circuit breaker
325
+ init_circuit_breaker "$PROJECT_ROOT"
326
+ check_circuit_breaker || return 1
327
+
328
+ # Layer 3: Recursion guard (if enabled)
329
+ if [[ "$enable_recursion_guard" == "true" ]]; then
330
+ init_recursion_guard "$PROJECT_ROOT"
331
+ check_recursion_guard || return 1
332
+ fi
333
+
334
+ # Layer 4: Lock (if required)
335
+ if [[ "$require_lock" == "true" ]]; then
336
+ acquire_hook_lock "$hook_name" "$PROJECT_ROOT" || return 1
337
+ fi
338
+
339
+ # Layer 5: Debouncing
340
+ check_debounce "$hook_name" "$PROJECT_ROOT" || return 1
341
+
342
+ # Initialize logging
343
+ init_log "hooks" "$PROJECT_ROOT"
344
+
345
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[HOOK] Environment ready: $hook_name" >&2
346
+ return 0
347
+ }
348
+
349
+ # ============================================================================
350
+ # 10. SAFE EXIT HANDLERS
351
+ # ============================================================================
352
+
353
+ # Always exit 0 from hooks (ADR-0157 Rule 6)
354
+ # Exception: PreToolUse guards exit 2 to block
355
+ hook_exit_success() {
356
+ release_hook_lock
357
+ release_recursion_guard
358
+ exit 0
359
+ }
360
+
361
+ hook_exit_block() {
362
+ release_hook_lock
363
+ release_recursion_guard
364
+ exit 2 # Block PreToolUse
365
+ }
366
+
367
+ # Record failure and exit safely
368
+ hook_exit_with_failure() {
369
+ local msg="$1"
370
+ record_circuit_breaker_failure
371
+ log_hook "ERROR" "$msg"
372
+ release_hook_lock
373
+ release_recursion_guard
374
+ exit 0 # Still exit 0 to not break Claude
375
+ }