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
@@ -167,11 +167,7 @@ fi
167
167
  if [ "$COMPLETING_TASK" = "false" ]; then
168
168
  echo "[$(date)] ⏭️ No tasks being completed, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
169
169
  rm -f "$STDIN_DATA"
170
- cat <<EOF
171
- {
172
- "continue": true
173
- }
174
- EOF
170
+ echo '{"continue":true}'
175
171
  exit 0
176
172
  fi
177
173
 
@@ -184,11 +180,7 @@ CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 base
184
180
  if [ -z "$CURRENT_INCREMENT" ]; then
185
181
  echo "[$(date)] ℹ️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
186
182
  rm -f "$STDIN_DATA"
187
- cat <<EOF
188
- {
189
- "continue": true
190
- }
191
- EOF
183
+ echo '{"continue":true}'
192
184
  exit 0
193
185
  fi
194
186
 
@@ -197,11 +189,7 @@ TASKS_MD=".specweave/increments/$CURRENT_INCREMENT/tasks.md"
197
189
  if [ ! -f "$TASKS_MD" ]; then
198
190
  echo "[$(date)] ℹ️ tasks.md not found for $CURRENT_INCREMENT (increment may be in planning stage)" >> "$DEBUG_LOG" 2>/dev/null || true
199
191
  rm -f "$STDIN_DATA"
200
- cat <<EOF
201
- {
202
- "continue": true
203
- }
204
- EOF
192
+ echo '{"continue":true}'
205
193
  exit 0
206
194
  fi
207
195
 
@@ -231,12 +219,7 @@ fi
231
219
  if [ -z "$VALIDATOR_SCRIPT" ] || ! command -v node &> /dev/null; then
232
220
  echo "[$(date)] ⚠️ AC test validator not found or Node.js missing" >> "$DEBUG_LOG" 2>/dev/null || true
233
221
  rm -f "$STDIN_DATA"
234
- cat <<EOF
235
- {
236
- "continue": true,
237
- "systemMessage": "⚠️ Warning: AC test validator not available. Task completion validation skipped. Install Node.js and rebuild SpecWeave to enable validation."
238
- }
239
- EOF
222
+ echo '{"continue":true,"systemMessage":"⚠️ Warning: AC test validator not available. Task completion validation skipped. Install Node.js and rebuild SpecWeave to enable validation."}'
240
223
  exit 0
241
224
  fi
242
225
 
@@ -262,16 +245,11 @@ if [ "$VALIDATION_EXIT_CODE" = "0" ]; then
262
245
  # Reset circuit breaker on success
263
246
  echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
264
247
 
265
- VALIDATION_SUMMARY=$(cat "$VALIDATION_OUTPUT" | tail -5 | tr '\n' ' ')
248
+ VALIDATION_SUMMARY=$(cat "$VALIDATION_OUTPUT" | tail -5 | tr '\n' ' ' | sed 's/"/\\"/g')
266
249
 
267
250
  rm -f "$VALIDATION_OUTPUT"
268
251
 
269
- cat <<EOF
270
- {
271
- "continue": true,
272
- "systemMessage": "✅ AC Test Validation Passed: All acceptance criteria have passing tests. Task completion allowed. ${VALIDATION_SUMMARY}"
273
- }
274
- EOF
252
+ printf '{"continue":true,"systemMessage":"✅ AC Test Validation Passed: All acceptance criteria have passing tests. Task completion allowed. %s"}\n' "$VALIDATION_SUMMARY"
275
253
  else
276
254
  # Validation failed - block completion
277
255
  echo "[$(date)] ❌ AC test validation failed" >> "$DEBUG_LOG" 2>/dev/null || true
@@ -280,18 +258,11 @@ else
280
258
  CURRENT_FAILURES=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
281
259
  echo "$((CURRENT_FAILURES + 1))" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
282
260
 
283
- VALIDATION_ERROR=$(cat "$VALIDATION_OUTPUT" | grep -A 10 "VALIDATION FAILED" | tr '\n' ' ' | cut -c 1-300)
261
+ VALIDATION_ERROR=$(cat "$VALIDATION_OUTPUT" | grep -A 10 "VALIDATION FAILED" | tr '\n' ' ' | cut -c 1-300 | sed 's/"/\\"/g')
284
262
 
285
263
  rm -f "$VALIDATION_OUTPUT"
286
264
 
287
- cat <<EOF
288
- {
289
- "continue": false,
290
- "systemMessage": "❌ AC TEST VALIDATION FAILED: Cannot mark task as complete until all acceptance criteria have passing tests. ${VALIDATION_ERROR}
291
-
292
- Fix the failing tests and try again. Run tests manually: npm test"
293
- }
294
- EOF
265
+ printf '{"continue":false,"systemMessage":"❌ AC TEST VALIDATION FAILED: Cannot mark task as complete until all acceptance criteria have passing tests. %s\\n\\nFix the failing tests and try again. Run tests manually: npm test"}\n' "$VALIDATION_ERROR"
295
266
  fi
296
267
 
297
268
  # ALWAYS exit 0 - NEVER let hook errors crash Claude Code
@@ -0,0 +1,194 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Pre-Task-Completion Hook
4
+ # CRITICAL QUALITY GATE: Validates AC tests before allowing task completion
5
+ #
6
+ # Runs automatically BEFORE any task is marked complete via TodoWrite
7
+ #
8
+ # WORKFLOW:
9
+ # =========
10
+ # 1. TodoWrite called with status="completed"
11
+ # 2. This hook fires (pre-completion validation)
12
+ # 3. Extract task ID from TodoWrite input
13
+ # 4. Find task in tasks.md
14
+ # 5. Run AC test validator
15
+ # 6. If tests PASS → Allow completion (continue: true)
16
+ # 7. If tests FAIL → Block completion (continue: false, show error)
17
+ #
18
+ # ENFORCEMENT:
19
+ # ============
20
+ # This is the ONLY way to mark tasks complete in SpecWeave.
21
+ # Manual edits to tasks.md are detected and flagged by pre-commit hooks.
22
+
23
+ set -e
24
+
25
+ # Find project root
26
+ find_project_root() {
27
+ local dir="$1"
28
+ while [ "$dir" != "/" ]; do
29
+ if [ -d "$dir/.specweave" ]; then
30
+ echo "$dir"
31
+ return 0
32
+ fi
33
+ dir="$(dirname "$dir")"
34
+ done
35
+ pwd
36
+ }
37
+
38
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
39
+ cd "$PROJECT_ROOT" 2>/dev/null || true
40
+
41
+ # ============================================================================
42
+ # CONFIGURATION
43
+ # ============================================================================
44
+
45
+ LOGS_DIR=".specweave/logs"
46
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
47
+
48
+ mkdir -p "$LOGS_DIR" 2>/dev/null || true
49
+
50
+ echo "[$(date)] 🔒 Pre-task-completion hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
51
+
52
+ # ============================================================================
53
+ # CAPTURE INPUT
54
+ # ============================================================================
55
+
56
+ STDIN_DATA=$(mktemp)
57
+ cat > "$STDIN_DATA"
58
+
59
+ echo "[$(date)] Input JSON:" >> "$DEBUG_LOG" 2>/dev/null || true
60
+ cat "$STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
61
+
62
+ # ============================================================================
63
+ # CHECK FOR TASK COMPLETION
64
+ # ============================================================================
65
+
66
+ # Only validate if a task is being marked complete
67
+ COMPLETING_TASK=false
68
+
69
+ if command -v jq >/dev/null 2>&1; then
70
+ # Check if any task is transitioning to "completed" status
71
+ COMPLETED_COUNT=$(jq -r '.tool_input.todos // [] | map(select(.status == "completed")) | length' "$STDIN_DATA" 2>/dev/null || echo "0")
72
+
73
+ if [ "$COMPLETED_COUNT" != "0" ]; then
74
+ COMPLETING_TASK=true
75
+ echo "[$(date)] ✓ Detected task completion (${COMPLETED_COUNT} tasks)" >> "$DEBUG_LOG" 2>/dev/null || true
76
+ fi
77
+ fi
78
+
79
+ # If no tasks being completed, allow without validation
80
+ if [ "$COMPLETING_TASK" = "false" ]; then
81
+ echo "[$(date)] ⏭️ No tasks being completed, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
82
+ rm -f "$STDIN_DATA"
83
+ cat <<EOF
84
+ {
85
+ "continue": true
86
+ }
87
+ EOF
88
+ exit 0
89
+ fi
90
+
91
+ # ============================================================================
92
+ # DETECT CURRENT INCREMENT
93
+ # ============================================================================
94
+
95
+ CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 basename | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
96
+
97
+ if [ -z "$CURRENT_INCREMENT" ]; then
98
+ echo "[$(date)] ℹ️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
99
+ rm -f "$STDIN_DATA"
100
+ cat <<EOF
101
+ {
102
+ "continue": true
103
+ }
104
+ EOF
105
+ exit 0
106
+ fi
107
+
108
+ TASKS_MD=".specweave/increments/$CURRENT_INCREMENT/tasks.md"
109
+
110
+ if [ ! -f "$TASKS_MD" ]; then
111
+ echo "[$(date)] ℹ️ tasks.md not found for $CURRENT_INCREMENT (increment may be in planning stage)" >> "$DEBUG_LOG" 2>/dev/null || true
112
+ rm -f "$STDIN_DATA"
113
+ cat <<EOF
114
+ {
115
+ "continue": true
116
+ }
117
+ EOF
118
+ exit 0
119
+ fi
120
+
121
+ # ============================================================================
122
+ # RUN AC TEST VALIDATION
123
+ # ============================================================================
124
+
125
+ echo "[$(date)] 🧪 Running AC test validation for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
126
+
127
+ # Determine which validation script to use
128
+ VALIDATOR_SCRIPT=""
129
+ if [ -f "$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js" ]; then
130
+ VALIDATOR_SCRIPT="$PROJECT_ROOT/dist/src/core/ac-test-validator-cli.js"
131
+ elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js" ]; then
132
+ VALIDATOR_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/src/core/ac-test-validator-cli.js"
133
+ elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js" ]; then
134
+ VALIDATOR_SCRIPT="${CLAUDE_PLUGIN_ROOT}/dist/src/core/ac-test-validator-cli.js"
135
+ fi
136
+
137
+ if [ -z "$VALIDATOR_SCRIPT" ] || ! command -v node &> /dev/null; then
138
+ echo "[$(date)] ⚠️ AC test validator not found or Node.js missing" >> "$DEBUG_LOG" 2>/dev/null || true
139
+ rm -f "$STDIN_DATA"
140
+ cat <<EOF
141
+ {
142
+ "continue": true,
143
+ "systemMessage": "⚠️ Warning: AC test validator not available. Task completion validation skipped. Install Node.js and rebuild SpecWeave to enable validation."
144
+ }
145
+ EOF
146
+ exit 0
147
+ fi
148
+
149
+ # Run validator (captures exit code)
150
+ VALIDATION_OUTPUT=$(mktemp)
151
+ VALIDATION_EXIT_CODE=0
152
+
153
+ (cd "$PROJECT_ROOT" && node "$VALIDATOR_SCRIPT" "$CURRENT_INCREMENT") > "$VALIDATION_OUTPUT" 2>&1 || VALIDATION_EXIT_CODE=$?
154
+
155
+ echo "[$(date)] Validator exit code: $VALIDATION_EXIT_CODE" >> "$DEBUG_LOG" 2>/dev/null || true
156
+ cat "$VALIDATION_OUTPUT" >> "$DEBUG_LOG" 2>/dev/null || true
157
+
158
+ rm -f "$STDIN_DATA"
159
+
160
+ # ============================================================================
161
+ # DECISION LOGIC
162
+ # ============================================================================
163
+
164
+ if [ "$VALIDATION_EXIT_CODE" = "0" ]; then
165
+ # Validation passed - allow completion
166
+ echo "[$(date)] ✅ AC test validation passed" >> "$DEBUG_LOG" 2>/dev/null || true
167
+
168
+ VALIDATION_SUMMARY=$(cat "$VALIDATION_OUTPUT" | tail -5 | tr '\n' ' ')
169
+
170
+ rm -f "$VALIDATION_OUTPUT"
171
+
172
+ cat <<EOF
173
+ {
174
+ "continue": true,
175
+ "systemMessage": "✅ AC Test Validation Passed: All acceptance criteria have passing tests. Task completion allowed. ${VALIDATION_SUMMARY}"
176
+ }
177
+ EOF
178
+ else
179
+ # Validation failed - block completion
180
+ echo "[$(date)] ❌ AC test validation failed" >> "$DEBUG_LOG" 2>/dev/null || true
181
+
182
+ VALIDATION_ERROR=$(cat "$VALIDATION_OUTPUT" | grep -A 10 "VALIDATION FAILED" | tr '\n' ' ' | cut -c 1-300)
183
+
184
+ rm -f "$VALIDATION_OUTPUT"
185
+
186
+ cat <<EOF
187
+ {
188
+ "continue": false,
189
+ "systemMessage": "❌ AC TEST VALIDATION FAILED: Cannot mark task as complete until all acceptance criteria have passing tests. ${VALIDATION_ERROR}
190
+
191
+ Fix the failing tests and try again. Run tests manually: npm test"
192
+ }
193
+ EOF
194
+ fi
@@ -128,18 +128,9 @@ rm -f "$STDIN_DATA"
128
128
  # ============================================================================
129
129
 
130
130
  if [ "$TOOL_NAME" = "AskUserQuestion" ]; then
131
- cat <<EOF
132
- {
133
- "continue": true,
134
- "systemMessage": "🔔 Sound played - user notified of question request"
135
- }
136
- EOF
131
+ echo '{"continue":true,"systemMessage":"🔔 Sound played - user notified of question request"}'
137
132
  else
138
- cat <<EOF
139
- {
140
- "continue": true
141
- }
142
- EOF
133
+ echo '{"continue":true}'
143
134
  fi
144
135
 
145
136
  # ALWAYS exit 0 - NEVER let hook errors crash Claude Code
@@ -0,0 +1,133 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Pre-Tool-Use Hook
4
+ # Runs BEFORE Claude calls any tool (PreToolUse event)
5
+ #
6
+ # PURPOSE: Detect when Claude asks questions via AskUserQuestion
7
+ # - Plays sound IMMEDIATELY when question is about to be asked
8
+ # - Complements post-task-completion hook (which only fires after TodoWrite)
9
+ # - Ensures user is always notified when Claude needs input
10
+ #
11
+ # SCOPE:
12
+ # - This hook fires for ALL tool calls (Read, Edit, Write, AskUserQuestion, etc.)
13
+ # - We filter for AskUserQuestion specifically to play sound
14
+ # - Non-blocking and fast (<10ms overhead)
15
+
16
+ set -e
17
+
18
+ # ============================================================================
19
+ # CONFIGURATION
20
+ # ============================================================================
21
+
22
+ # Find project root
23
+ find_project_root() {
24
+ local dir="$1"
25
+ while [ "$dir" != "/" ]; do
26
+ if [ -d "$dir/.specweave" ]; then
27
+ echo "$dir"
28
+ return 0
29
+ fi
30
+ dir="$(dirname "$dir")"
31
+ done
32
+ # Fallback
33
+ if [ -d "$(pwd)/.specweave" ]; then
34
+ pwd
35
+ else
36
+ echo "$(pwd)"
37
+ fi
38
+ }
39
+
40
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
41
+ cd "$PROJECT_ROOT" 2>/dev/null || true
42
+
43
+ LOGS_DIR=".specweave/logs"
44
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
45
+
46
+ mkdir -p "$LOGS_DIR" 2>/dev/null || true
47
+
48
+ # ============================================================================
49
+ # CAPTURE INPUT (Tool Call Details)
50
+ # ============================================================================
51
+
52
+ STDIN_DATA=$(mktemp)
53
+ cat > "$STDIN_DATA"
54
+
55
+ # Log the tool call for debugging
56
+ echo "[$(date)] 🔧 PreToolUse hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
57
+ echo "[$(date)] Tool call JSON:" >> "$DEBUG_LOG" 2>/dev/null || true
58
+ cat "$STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
59
+ echo "" >> "$DEBUG_LOG" 2>/dev/null || true
60
+
61
+ # ============================================================================
62
+ # DETECT AskUserQuestion TOOL
63
+ # ============================================================================
64
+
65
+ TOOL_NAME=""
66
+
67
+ if command -v jq >/dev/null 2>&1; then
68
+ # Use jq if available (most reliable)
69
+ TOOL_NAME=$(jq -r '.tool_name // empty' "$STDIN_DATA" 2>/dev/null)
70
+ else
71
+ # Fallback: grep-based detection
72
+ if grep -q '"tool_name"' "$STDIN_DATA" 2>/dev/null; then
73
+ TOOL_NAME=$(grep -o '"tool_name":"[^"]*"' "$STDIN_DATA" | cut -d'"' -f4)
74
+ fi
75
+ fi
76
+
77
+ echo "[$(date)] Tool name: $TOOL_NAME" >> "$DEBUG_LOG" 2>/dev/null || true
78
+
79
+ # ============================================================================
80
+ # PLAY SOUND IF AskUserQuestion
81
+ # ============================================================================
82
+
83
+ play_sound() {
84
+ case "$(uname -s)" in
85
+ Darwin)
86
+ # macOS: Use afplay with a distinctive sound for questions
87
+ afplay /System/Library/Sounds/Tink.aiff 2>/dev/null || true
88
+ ;;
89
+ Linux)
90
+ # Linux: Use paplay or aplay
91
+ paplay /usr/share/sounds/freedesktop/stereo/dialog-question.oga 2>/dev/null || \
92
+ paplay /usr/share/sounds/freedesktop/stereo/message-new-instant.oga 2>/dev/null || \
93
+ aplay /usr/share/sounds/alsa/Front_Center.wav 2>/dev/null || true
94
+ ;;
95
+ MINGW*|MSYS*|CYGWIN*)
96
+ # Windows: Use PowerShell
97
+ powershell -c "(New-Object Media.SoundPlayer 'C:\Windows\Media\Windows Notify.wav').PlaySync();" 2>/dev/null || true
98
+ ;;
99
+ esac
100
+ }
101
+
102
+ if [ "$TOOL_NAME" = "AskUserQuestion" ]; then
103
+ echo "[$(date)] 🔔 QUESTION DETECTED! Playing notification sound" >> "$DEBUG_LOG" 2>/dev/null || true
104
+ play_sound
105
+
106
+ # Log this event
107
+ echo "[$(date)] Claude is asking for user input via AskUserQuestion" >> "$DEBUG_LOG" 2>/dev/null || true
108
+ fi
109
+
110
+ # ============================================================================
111
+ # CLEANUP
112
+ # ============================================================================
113
+
114
+ rm -f "$STDIN_DATA"
115
+
116
+ # ============================================================================
117
+ # OUTPUT TO CLAUDE (Always continue)
118
+ # ============================================================================
119
+
120
+ if [ "$TOOL_NAME" = "AskUserQuestion" ]; then
121
+ cat <<EOF
122
+ {
123
+ "continue": true,
124
+ "systemMessage": "🔔 Sound played - user notified of question request"
125
+ }
126
+ EOF
127
+ else
128
+ cat <<EOF
129
+ {
130
+ "continue": true
131
+ }
132
+ EOF
133
+ fi
@@ -12,6 +12,7 @@
12
12
  * - session-start
13
13
  * - post-tool-use
14
14
  * - completion-guard
15
+ * - bash-file-guard
15
16
  *
16
17
  * @module hooks/universal/dispatcher
17
18
  */
@@ -27,18 +28,82 @@ const __dirname = dirname(__filename);
27
28
  // Hook type from arguments
28
29
  const hookType = process.argv[2] || 'unknown';
29
30
 
31
+ // Global timeout for hook execution (30 seconds max)
32
+ const HOOK_TIMEOUT_MS = 30000;
33
+
34
+ /**
35
+ * Detect if we're in a development environment with duplicate hooks
36
+ *
37
+ * Problem: When working IN the specweave project AND having the marketplace
38
+ * plugin installed, Claude Code loads hooks from BOTH sources, causing:
39
+ * - Duplicate hook execution
40
+ * - Potential race conditions
41
+ * - Confusing output
42
+ *
43
+ * Solution: If we detect we're running from the marketplace copy while
44
+ * the CWD is the specweave project itself, skip execution (let local hooks handle it).
45
+ *
46
+ * @returns {boolean} true if we should skip this hook execution
47
+ */
48
+ function shouldSkipDueToDevEnvironment() {
49
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || '';
50
+ const cwd = process.cwd();
51
+
52
+ // Check if running from marketplace (installed plugin)
53
+ const isFromMarketplace = pluginRoot.includes('.claude/plugins/marketplaces/');
54
+
55
+ // Check if CWD is the specweave project itself
56
+ const isInSpecweaveProject = existsSync(join(cwd, 'plugins', 'specweave', 'hooks', 'hooks.json'));
57
+
58
+ // Skip marketplace hooks when developing specweave locally
59
+ if (isFromMarketplace && isInSpecweaveProject) {
60
+ // Output diagnostic only for session-start (once per session)
61
+ if (hookType === 'session-start') {
62
+ console.log(JSON.stringify({
63
+ continue: true,
64
+ systemMessage: 'SpecWeave dev environment: Using local hooks (marketplace hooks skipped)'
65
+ }));
66
+ } else {
67
+ console.log(JSON.stringify({ continue: true }));
68
+ }
69
+ return true;
70
+ }
71
+
72
+ return false;
73
+ }
74
+
30
75
  /**
31
- * Find the dist/hooks directory
76
+ * Wrap a promise with a timeout
77
+ * @param promise - Promise to wrap
78
+ * @param timeoutMs - Timeout in milliseconds
79
+ * @param cleanup - Optional cleanup function (e.g., to kill child process)
80
+ */
81
+ function withTimeout(promise, timeoutMs, cleanup) {
82
+ let timeoutId;
83
+ const timeoutPromise = new Promise((_, reject) => {
84
+ timeoutId = setTimeout(() => {
85
+ if (cleanup) cleanup();
86
+ reject(new Error(`Hook timeout after ${timeoutMs}ms`));
87
+ }, timeoutMs);
88
+ });
89
+
90
+ return Promise.race([promise, timeoutPromise]).finally(() => {
91
+ clearTimeout(timeoutId);
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Find the dist/src/hooks directory (TypeScript compiled hooks)
32
97
  */
33
98
  function findHooksDir() {
34
99
  // Try multiple locations
35
100
  const candidates = [
36
- // Production: node_modules/specweave/dist/hooks
37
- join(__dirname, '..', '..', '..', '..', 'node_modules', 'specweave', 'dist', 'hooks'),
38
- // Development: project root dist/hooks
101
+ // Production: node_modules/specweave/dist/src/hooks
102
+ join(__dirname, '..', '..', '..', '..', 'node_modules', 'specweave', 'dist', 'src', 'hooks'),
103
+ // Development: project root dist/src/hooks
104
+ join(__dirname, '..', '..', '..', '..', 'dist', 'src', 'hooks'),
105
+ // Fallback: older path without src/ (backward compatibility)
39
106
  join(__dirname, '..', '..', '..', '..', 'dist', 'hooks'),
40
- // Relative to this file
41
- join(__dirname, '..', '..', '..', '..', 'src', 'hooks'),
42
107
  ];
43
108
 
44
109
  for (const candidate of candidates) {
@@ -52,7 +117,7 @@ function findHooksDir() {
52
117
  }
53
118
 
54
119
  /**
55
- * Run a hook script
120
+ * Run a hook script with timeout protection
56
121
  */
57
122
  async function runHook(scriptName) {
58
123
  const hooksDir = findHooksDir();
@@ -74,13 +139,26 @@ async function runHook(scriptName) {
74
139
  windowsHide: true,
75
140
  });
76
141
 
77
- return new Promise((resolve) => {
142
+ const execPromise = new Promise((resolve) => {
78
143
  child.on('exit', (code) => resolve(code || 0));
79
144
  child.on('error', () => {
80
145
  console.log(JSON.stringify({ continue: true }));
81
146
  resolve(1);
82
147
  });
83
148
  });
149
+
150
+ try {
151
+ return await withTimeout(execPromise, HOOK_TIMEOUT_MS, () => {
152
+ // Kill the child process on timeout
153
+ try {
154
+ child.kill('SIGTERM');
155
+ setTimeout(() => child.kill('SIGKILL'), 1000);
156
+ } catch { /* ignore */ }
157
+ });
158
+ } catch (err) {
159
+ console.log(JSON.stringify({ continue: true, error: err.message }));
160
+ return 1;
161
+ }
84
162
  }
85
163
 
86
164
  /**
@@ -124,7 +202,39 @@ function findGitBash() {
124
202
  }
125
203
 
126
204
  /**
127
- * Fallback to bash script if TypeScript not built
205
+ * Helper to spawn a bash script with timeout protection
206
+ */
207
+ async function spawnBashWithTimeout(bashExe, args, options = {}) {
208
+ const child = spawn(bashExe, args, {
209
+ stdio: ['inherit', 'inherit', 'inherit'],
210
+ windowsHide: true,
211
+ ...options,
212
+ });
213
+
214
+ const execPromise = new Promise((resolve) => {
215
+ child.on('exit', (code) => resolve(code || 0));
216
+ child.on('error', () => {
217
+ console.log(JSON.stringify({ continue: true }));
218
+ resolve(1);
219
+ });
220
+ });
221
+
222
+ try {
223
+ return await withTimeout(execPromise, HOOK_TIMEOUT_MS, () => {
224
+ // Kill the child process on timeout
225
+ try {
226
+ child.kill('SIGTERM');
227
+ setTimeout(() => child.kill('SIGKILL'), 1000);
228
+ } catch { /* ignore */ }
229
+ });
230
+ } catch (err) {
231
+ console.log(JSON.stringify({ continue: true, error: err.message }));
232
+ return 1;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Fallback to bash script if TypeScript not built (with timeout protection)
128
238
  *
129
239
  * @param bashScript - Name of the bash script (e.g., 'post-tool-use.sh')
130
240
  * @param subdir - Subdirectory under v2 (e.g., 'dispatchers', 'guards')
@@ -143,17 +253,7 @@ async function fallbackToBash(bashScript, subdir = 'dispatchers') {
143
253
  // Strategy 1: Git Bash (preferred - most common)
144
254
  const bashExe = findGitBash();
145
255
  if (bashExe) {
146
- const child = spawn(bashExe, [scriptPath], {
147
- stdio: ['inherit', 'inherit', 'inherit'],
148
- windowsHide: true,
149
- });
150
- return new Promise((resolve) => {
151
- child.on('exit', (code) => resolve(code || 0));
152
- child.on('error', () => {
153
- console.log(JSON.stringify({ continue: true }));
154
- resolve(1);
155
- });
156
- });
256
+ return spawnBashWithTimeout(bashExe, [scriptPath]);
157
257
  }
158
258
 
159
259
  // Strategy 2: WSL (if Git Bash not available) - FALLBACK only
@@ -166,17 +266,7 @@ async function fallbackToBash(bashScript, subdir = 'dispatchers') {
166
266
  const wslScriptPath = scriptPath
167
267
  .replace(/\\/g, '/')
168
268
  .replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`);
169
- const child = spawn('wsl', ['bash', wslScriptPath], {
170
- stdio: ['inherit', 'inherit', 'inherit'],
171
- windowsHide: true,
172
- });
173
- return new Promise((resolve) => {
174
- child.on('exit', (code) => resolve(code || 0));
175
- child.on('error', () => {
176
- console.log(JSON.stringify({ continue: true }));
177
- resolve(1);
178
- });
179
- });
269
+ return spawnBashWithTimeout('wsl', ['bash', wslScriptPath]);
180
270
  }
181
271
 
182
272
  // Strategy 3: No bash available - output warning and continue
@@ -188,21 +278,18 @@ async function fallbackToBash(bashScript, subdir = 'dispatchers') {
188
278
  return;
189
279
  }
190
280
 
191
- // POSIX (macOS, Linux) - run directly
192
- const child = spawn('bash', [scriptPath], {
193
- stdio: ['inherit', 'inherit', 'inherit'],
194
- });
195
- return new Promise((resolve) => {
196
- child.on('exit', (code) => resolve(code || 0));
197
- child.on('error', () => {
198
- console.log(JSON.stringify({ continue: true }));
199
- resolve(1);
200
- });
201
- });
281
+ // POSIX (macOS, Linux) - run directly with timeout
282
+ return spawnBashWithTimeout('bash', [scriptPath]);
202
283
  }
203
284
 
204
285
  // Main routing
205
286
  async function main() {
287
+ // CRITICAL: Skip if we're in dev environment with duplicate hooks
288
+ // This prevents marketplace hooks from running when local hooks are available
289
+ if (shouldSkipDueToDevEnvironment()) {
290
+ return;
291
+ }
292
+
206
293
  const isWindows = process.platform === 'win32';
207
294
 
208
295
  try {
@@ -235,6 +322,12 @@ async function main() {
235
322
  await fallbackToBash('completion-guard.sh', 'guards');
236
323
  break;
237
324
 
325
+ case 'bash-file-guard':
326
+ // CRITICAL: Prevents infinite hangs from heredoc/echo file creation
327
+ // See CLAUDE.md Rule 9 for why this is essential
328
+ await fallbackToBash('bash-file-guard.sh', 'guards');
329
+ break;
330
+
238
331
  default:
239
332
  console.log(JSON.stringify({ continue: true, error: `Unknown hook type: ${hookType}` }));
240
333
  }