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
@@ -0,0 +1,183 @@
1
+ #!/bin/bash
2
+ # fail-fast-wrapper.sh - HARD TIMEOUT wrapper for all hooks
3
+ # If ANY hook takes longer than HOOK_TIMEOUT, it gets KILLED.
4
+ #
5
+ # Usage: bash fail-fast-wrapper.sh <hook-script> [args...]
6
+ #
7
+ # Environment:
8
+ # HOOK_TIMEOUT - max seconds (default: 5)
9
+ # HOOK_DEBUG - set to 1 for verbose logging
10
+ #
11
+ # Exit behavior:
12
+ # - Returns hook output on success
13
+ # - Returns safe JSON on timeout ({"continue":true} or {"decision":"approve"})
14
+ # - NEVER hangs - timeout is enforced with SIGKILL
15
+ #
16
+ # CRASH PREVENTION:
17
+ # - Integrates with crash-prevention.sh for process storm detection
18
+ # - Auto-kills zombie processes on timeout
19
+ # - Records failures for circuit breaker
20
+ #
21
+ # v0.33.0 - Enhanced with crash prevention integration
22
+
23
+ set -o pipefail
24
+
25
+ # === Configuration ===
26
+ HOOK_TIMEOUT="${HOOK_TIMEOUT:-5}" # 5 seconds - more than enough for any hook
27
+ HOOK_DEBUG="${HOOK_DEBUG:-0}"
28
+ LOG_FILE="${HOME}/.claude/hook-failures.log"
29
+
30
+ # === Crash Prevention Integration ===
31
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
32
+ CRASH_PREVENTION="${SCRIPT_DIR}/../lib/crash-prevention.sh"
33
+
34
+ # Source crash prevention if available (non-blocking)
35
+ if [[ -f "$CRASH_PREVENTION" ]]; then
36
+ source "$CRASH_PREVENTION" 2>/dev/null || true
37
+ fi
38
+
39
+ # === Helper functions ===
40
+ log_debug() {
41
+ [[ "$HOOK_DEBUG" == "1" ]] && echo "[DEBUG $(date +%H:%M:%S)] $*" >&2
42
+ }
43
+
44
+ log_failure() {
45
+ local msg="$1"
46
+ mkdir -p "$(dirname "$LOG_FILE")"
47
+ echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] HOOK TIMEOUT: $msg" >> "$LOG_FILE"
48
+ }
49
+
50
+ # === Safe JSON output based on hook type ===
51
+ get_safe_output() {
52
+ local script="$1"
53
+ # PreToolUse hooks need "decision" format
54
+ if [[ "$script" == *"guard"* ]] || [[ "$script" == *"validator"* ]] || [[ "$script" == *"PreToolUse"* ]]; then
55
+ echo '{"decision":"allow"}'
56
+ else
57
+ echo '{"continue":true}'
58
+ fi
59
+ }
60
+
61
+ # === Read stdin with timeout ===
62
+ # Critical: stdin can block forever if not handled properly
63
+ read_stdin_with_timeout() {
64
+ local stdin_content=""
65
+
66
+ # Use read with timeout (integer seconds for bash compatibility)
67
+ if read -t 1 -r line; then
68
+ stdin_content="$line"
69
+ # Continue reading remaining lines (no timeout - stdin should be closed)
70
+ while IFS= read -r line; do
71
+ stdin_content="${stdin_content}"$'\n'"${line}"
72
+ done
73
+ fi
74
+
75
+ echo "$stdin_content"
76
+ }
77
+
78
+ # === Main execution ===
79
+ main() {
80
+ local script="$1"
81
+ shift
82
+ local args=("$@")
83
+
84
+ if [[ -z "$script" ]]; then
85
+ echo '{"continue":true}'
86
+ exit 0
87
+ fi
88
+
89
+ if [[ ! -f "$script" ]]; then
90
+ log_debug "Script not found: $script"
91
+ echo '{"continue":true}'
92
+ exit 0
93
+ fi
94
+
95
+ # === CRASH PREVENTION: Process Storm Detection ===
96
+ # If too many hooks are running, skip this one to prevent cascade
97
+ if type detect_process_storm &>/dev/null; then
98
+ local storm_status
99
+ storm_status=$(detect_process_storm 25)
100
+ if [[ "$storm_status" == STORM* ]]; then
101
+ log_failure "$script - BLOCKED due to process storm: $storm_status"
102
+ get_safe_output "$script"
103
+ exit 0
104
+ fi
105
+ fi
106
+
107
+ log_debug "Executing: $script (timeout: ${HOOK_TIMEOUT}s)"
108
+
109
+ # Read stdin first (with its own timeout)
110
+ local stdin_content
111
+ stdin_content=$(read_stdin_with_timeout)
112
+
113
+ # Execute the hook with hard timeout
114
+ # Using timeout with --kill-after to ensure SIGKILL if SIGTERM doesn't work
115
+ local output
116
+ local exit_code
117
+
118
+ # Create temp file for output (avoid subshell issues)
119
+ local tmp_out
120
+ tmp_out=$(mktemp)
121
+
122
+ # Run with timeout - kill entire process group on timeout
123
+ if command -v gtimeout >/dev/null 2>&1; then
124
+ # macOS with coreutils
125
+ echo "$stdin_content" | gtimeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
126
+ exit_code=$?
127
+ elif command -v timeout >/dev/null 2>&1; then
128
+ # Linux
129
+ echo "$stdin_content" | timeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
130
+ exit_code=$?
131
+ else
132
+ # Fallback: manual timeout using background process
133
+ (
134
+ echo "$stdin_content" | bash "$script" "${args[@]}" > "$tmp_out" 2>/dev/null
135
+ ) &
136
+ local pid=$!
137
+
138
+ # Wait with timeout
139
+ local count=0
140
+ while kill -0 "$pid" 2>/dev/null && [[ $count -lt $((HOOK_TIMEOUT * 10)) ]]; do
141
+ sleep 0.1
142
+ count=$((count + 1))
143
+ done
144
+
145
+ if kill -0 "$pid" 2>/dev/null; then
146
+ # Still running - kill it!
147
+ kill -9 "$pid" 2>/dev/null
148
+ wait "$pid" 2>/dev/null
149
+ exit_code=124 # timeout exit code
150
+ else
151
+ wait "$pid"
152
+ exit_code=$?
153
+ fi
154
+ fi
155
+
156
+ output=$(cat "$tmp_out" 2>/dev/null)
157
+ rm -f "$tmp_out"
158
+
159
+ # Handle timeout (exit code 124 or 137)
160
+ if [[ $exit_code -eq 124 ]] || [[ $exit_code -eq 137 ]]; then
161
+ log_failure "$script - killed after ${HOOK_TIMEOUT}s"
162
+ log_debug "TIMEOUT: $script killed after ${HOOK_TIMEOUT}s"
163
+
164
+ # === CRASH PREVENTION: Clean up potential zombie processes ===
165
+ if type kill_zombie_heredocs &>/dev/null; then
166
+ kill_zombie_heredocs 2>/dev/null || true
167
+ fi
168
+
169
+ get_safe_output "$script"
170
+ exit 0
171
+ fi
172
+
173
+ # Return output or safe default
174
+ if [[ -n "$output" ]]; then
175
+ echo "$output"
176
+ else
177
+ get_safe_output "$script"
178
+ fi
179
+
180
+ exit 0
181
+ }
182
+
183
+ main "$@"
@@ -1,26 +1,26 @@
1
- @echo off
2
- REM hook-wrapper.cmd - Windows resilient hook launcher
3
- REM Prevents crashes when dispatcher.mjs is temporarily unavailable
4
-
5
- setlocal enabledelayedexpansion
6
-
7
- set "HOOK_TYPE=%~1"
8
- if "%HOOK_TYPE%"=="" set "HOOK_TYPE=unknown"
9
-
10
- set "SCRIPT_DIR=%~dp0"
11
- set "DISPATCHER=%SCRIPT_DIR%dispatcher.mjs"
12
-
13
- REM Check if dispatcher exists
14
- if not exist "%DISPATCHER%" (
15
- echo {"continue":true,"systemMessage":"Hook skipped: dispatcher.mjs not found"}
16
- exit /b 0
17
- )
18
-
19
- REM Run dispatcher with error suppression
20
- node "%DISPATCHER%" "%HOOK_TYPE%" 2>nul
21
- if errorlevel 1 (
22
- echo {"continue":true,"systemMessage":"Hook error, continuing"}
23
- exit /b 0
24
- )
25
-
26
- exit /b 0
1
+ @echo off
2
+ REM hook-wrapper.cmd - Windows resilient hook launcher
3
+ REM Prevents crashes when dispatcher.mjs is temporarily unavailable
4
+
5
+ setlocal enabledelayedexpansion
6
+
7
+ set "HOOK_TYPE=%~1"
8
+ if "%HOOK_TYPE%"=="" set "HOOK_TYPE=unknown"
9
+
10
+ set "SCRIPT_DIR=%~dp0"
11
+ set "DISPATCHER=%SCRIPT_DIR%dispatcher.mjs"
12
+
13
+ REM Check if dispatcher exists
14
+ if not exist "%DISPATCHER%" (
15
+ echo {"continue":true,"systemMessage":"Hook skipped: dispatcher.mjs not found"}
16
+ exit /b 0
17
+ )
18
+
19
+ REM Run dispatcher with error suppression
20
+ node "%DISPATCHER%" "%HOOK_TYPE%" 2>nul
21
+ if errorlevel 1 (
22
+ echo {"continue":true,"systemMessage":"Hook error, continuing"}
23
+ exit /b 0
24
+ )
25
+
26
+ exit /b 0
@@ -1,16 +1,16 @@
1
- @echo off
2
- :: Universal Session Start Hook for Windows
3
- :: Calls the Node.js dispatcher
4
-
5
- :: Find node.exe
6
- where node >nul 2>&1
7
- if %ERRORLEVEL% neq 0 (
8
- echo {"continue": true, "error": "Node.js not found"}
9
- exit /b 0
10
- )
11
-
12
- :: Get the directory of this script
13
- set "SCRIPT_DIR=%~dp0"
14
-
15
- :: Run the dispatcher
16
- node "%SCRIPT_DIR%dispatcher.mjs" session-start
1
+ @echo off
2
+ :: Universal Session Start Hook for Windows
3
+ :: Calls the Node.js dispatcher
4
+
5
+ :: Find node.exe
6
+ where node >nul 2>&1
7
+ if %ERRORLEVEL% neq 0 (
8
+ echo {"continue": true, "error": "Node.js not found"}
9
+ exit /b 0
10
+ )
11
+
12
+ :: Get the directory of this script
13
+ set "SCRIPT_DIR=%~dp0"
14
+
15
+ :: Run the dispatcher
16
+ node "%SCRIPT_DIR%dispatcher.mjs" session-start
@@ -1,16 +1,16 @@
1
- # Universal Session Start Hook for Windows PowerShell
2
- # Calls the Node.js dispatcher for cross-platform compatibility
3
-
4
- # Find node.exe
5
- $nodePath = Get-Command node -ErrorAction SilentlyContinue
6
-
7
- if (-not $nodePath) {
8
- Write-Host '{"continue": true, "error": "Node.js not found"}'
9
- exit 0
10
- }
11
-
12
- # Get script directory
13
- $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
14
-
15
- # Run the dispatcher
16
- & node "$scriptDir\dispatcher.mjs" session-start
1
+ # Universal Session Start Hook for Windows PowerShell
2
+ # Calls the Node.js dispatcher for cross-platform compatibility
3
+
4
+ # Find node.exe
5
+ $nodePath = Get-Command node -ErrorAction SilentlyContinue
6
+
7
+ if (-not $nodePath) {
8
+ Write-Host '{"continue": true, "error": "Node.js not found"}'
9
+ exit 0
10
+ }
11
+
12
+ # Get script directory
13
+ $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
14
+
15
+ # Run the dispatcher
16
+ & node "$scriptDir\dispatcher.mjs" session-start
@@ -1,17 +1,17 @@
1
1
  #!/bin/bash
2
2
 
3
- # SpecWeave UserPromptSubmit Hook (v0.26.13 - ULTRA-OPTIMIZED)
3
+ # SpecWeave UserPromptSubmit Hook (v0.33.0 - SCRIPT DELEGATION)
4
4
  # Fires BEFORE user's command executes (prompt-based hook)
5
5
  # Purpose: Discipline validation, context injection, command suggestions
6
6
  #
7
- # OPTIMIZATIONS (v0.26.13):
8
- # 1. jq for JSON parsing (10x faster than node -e)
9
- # 2. Single active increment detection (cached, not 4x!)
10
- # 3. Removed redundant find | while loops
11
- # 4. Deferred heavy checks (SpecSyncManager only when needed)
12
- # 5. Ultra-fast early exits
7
+ # FEATURES:
8
+ # - v0.33.0: Script delegation - status commands bypass LLM entirely (<1s)
9
+ # - v0.26.13: jq for JSON parsing (10x faster than node -e)
10
+ # - Single active increment detection (cached, not 4x!)
11
+ # - Deferred heavy checks (SpecSyncManager only when needed)
12
+ # - Ultra-fast early exits
13
13
  #
14
- # Performance: <10ms (most prompts) vs 200-500ms (before)
14
+ # Performance: Status commands <1s (was 3+ min), other prompts <10ms
15
15
 
16
16
  set +e
17
17
 
@@ -45,6 +45,131 @@ if [[ ! -d "$SPECWEAVE_DIR" ]]; then
45
45
  exit 0
46
46
  fi
47
47
 
48
+ # ==============================================================================
49
+ # INSTANT SCRIPT EXECUTION: Status commands bypass LLM entirely (v0.33.0)
50
+ # ==============================================================================
51
+ # These commands need NO LLM reasoning - execute scripts directly for <1s response
52
+ # Pattern: Detect command → Execute script → Return output via "block" → Exit
53
+
54
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
55
+ SCRIPTS_DIR="$PLUGIN_ROOT/scripts"
56
+
57
+ # Helper: Escape output for JSON (handles newlines, quotes, backslashes)
58
+ escape_json() {
59
+ local input="$1"
60
+ # Escape backslashes, then quotes, then newlines
61
+ echo "$input" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//'
62
+ }
63
+
64
+ # /specweave:jobs → Execute read-jobs.sh (pure bash, ~2ms)
65
+ if echo "$PROMPT" | grep -qE "^/specweave:jobs($| )"; then
66
+ ARGS=$(echo "$PROMPT" | sed 's|^/specweave:jobs\s*||')
67
+ if [[ -f "$SCRIPTS_DIR/read-jobs.sh" ]]; then
68
+ OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-jobs.sh" $ARGS 2>&1)
69
+ elif [[ -f "$SCRIPTS_DIR/jobs.js" ]] && command -v node >/dev/null 2>&1; then
70
+ OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/jobs.js" $ARGS 2>&1)
71
+ else
72
+ OUTPUT="❌ No jobs script available"
73
+ fi
74
+ OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
75
+ printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
76
+ exit 0
77
+ fi
78
+
79
+ # /specweave:progress → Execute read-progress.sh (pure bash, ~30ms)
80
+ if echo "$PROMPT" | grep -qE "^/specweave:progress($| )"; then
81
+ ARGS=$(echo "$PROMPT" | sed 's|^/specweave:progress\s*||')
82
+ if [[ -f "$SCRIPTS_DIR/read-progress.sh" ]]; then
83
+ OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-progress.sh" $ARGS 2>&1)
84
+ elif [[ -f "$SCRIPTS_DIR/progress.js" ]] && command -v node >/dev/null 2>&1; then
85
+ OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/progress.js" $ARGS 2>&1)
86
+ else
87
+ OUTPUT="❌ No progress script available"
88
+ fi
89
+ OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
90
+ printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
91
+ exit 0
92
+ fi
93
+
94
+ # /specweave:status → Execute read-status.sh (pure bash, ~150ms)
95
+ if echo "$PROMPT" | grep -qE "^/specweave:status($| )"; then
96
+ ARGS=$(echo "$PROMPT" | sed 's|^/specweave:status\s*||')
97
+ if [[ -f "$SCRIPTS_DIR/read-status.sh" ]]; then
98
+ OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-status.sh" $ARGS 2>&1)
99
+ elif [[ -f "$SCRIPTS_DIR/status.js" ]] && command -v node >/dev/null 2>&1; then
100
+ OUTPUT=$(cd "$(pwd)" && node "$SCRIPTS_DIR/status.js" $ARGS 2>&1)
101
+ else
102
+ OUTPUT="❌ No status script available"
103
+ fi
104
+ OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
105
+ printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
106
+ exit 0
107
+ fi
108
+
109
+ # /specweave:workflow → Execute read-workflow.sh (pure bash, ~100ms)
110
+ if echo "$PROMPT" | grep -qE "^/specweave:workflow($| )"; then
111
+ ARGS=$(echo "$PROMPT" | sed 's|^/specweave:workflow\s*||')
112
+ if [[ -f "$SCRIPTS_DIR/read-workflow.sh" ]]; then
113
+ OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-workflow.sh" $ARGS 2>&1)
114
+ else
115
+ OUTPUT="❌ No workflow script available"
116
+ fi
117
+ OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
118
+ printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
119
+ exit 0
120
+ fi
121
+
122
+ # /specweave:costs → Execute read-costs.sh (pure bash, ~50ms)
123
+ if echo "$PROMPT" | grep -qE "^/specweave:costs($| )"; then
124
+ ARGS=$(echo "$PROMPT" | sed 's|^/specweave:costs\s*||')
125
+ if [[ -f "$SCRIPTS_DIR/read-costs.sh" ]]; then
126
+ OUTPUT=$(cd "$(pwd)" && bash "$SCRIPTS_DIR/read-costs.sh" $ARGS 2>&1)
127
+ else
128
+ OUTPUT="❌ No costs script available"
129
+ fi
130
+ OUTPUT_ESCAPED=$(escape_json "$OUTPUT")
131
+ printf '{"decision":"block","reason":"%s"}\n' "$OUTPUT_ESCAPED"
132
+ exit 0
133
+ fi
134
+
135
+ # ==============================================================================
136
+ # TASK COUNT GUARD: Block /specweave:do for oversized increments (v0.32.2+)
137
+ # ==============================================================================
138
+ # >8 tasks = context explosion = CRASH (per CLAUDE.md)
139
+ MAX_TASKS=8
140
+
141
+ if echo "$PROMPT" | grep -qE "^/specweave:do($| )"; then
142
+ # Extract increment ID from prompt
143
+ DO_INCREMENT_ID=$(echo "$PROMPT" | grep -oE "[0-9]{4}[a-zA-Z0-9-]*" | head -1)
144
+
145
+ # If no ID provided, find active increment
146
+ if [[ -z "$DO_INCREMENT_ID" ]]; then
147
+ for meta in "$SPECWEAVE_DIR/increments"/*/metadata.json; do
148
+ [[ -f "$meta" ]] || continue
149
+ if command -v jq >/dev/null 2>&1; then
150
+ status=$(jq -r '.status // "unknown"' "$meta" 2>/dev/null)
151
+ else
152
+ status=$(grep -oP '"status"\s*:\s*"\K[^"]*' "$meta" 2>/dev/null || echo "unknown")
153
+ fi
154
+ if [[ "$status" == "active" || "$status" == "in-progress" ]]; then
155
+ DO_INCREMENT_ID=$(basename "$(dirname "$meta")")
156
+ break
157
+ fi
158
+ done
159
+ fi
160
+
161
+ if [[ -n "$DO_INCREMENT_ID" ]]; then
162
+ TASKS_FILE="$SPECWEAVE_DIR/increments/$DO_INCREMENT_ID/tasks.md"
163
+ if [[ -f "$TASKS_FILE" ]]; then
164
+ TASK_COUNT=$(grep -c "^### T-" "$TASKS_FILE" 2>/dev/null || echo "0")
165
+ if [[ "$TASK_COUNT" -gt "$MAX_TASKS" ]]; then
166
+ printf '{"decision":"block","reason":"❌ TASK COUNT EXCEEDS LIMIT\\n\\nIncrement %s has %s tasks (maximum: %s)\\n\\n>8 tasks = context explosion = CRASH\\n\\n💡 REQUIRED: Split into smaller increments:\\n\\n Pattern: %s/ → Split into:\\n • %s-part1/ (T-001 to T-004)\\n • Next increment (T-005 to T-008)\\n • Next increment (T-009+)\\n\\n⚠️ DO NOT PROCEED until tasks.md has ≤8 tasks!"}\n' "$DO_INCREMENT_ID" "$TASK_COUNT" "$MAX_TASKS" "$DO_INCREMENT_ID" "$DO_INCREMENT_ID"
167
+ exit 0
168
+ fi
169
+ fi
170
+ fi
171
+ fi
172
+
48
173
  # ==============================================================================
49
174
  # CACHED ACTIVE INCREMENT DETECTION (ONCE - reused throughout!)
50
175
  # ==============================================================================
@@ -101,23 +226,13 @@ if echo "$PROMPT" | grep -q "/specweave:increment"; then
101
226
 
102
227
  # Above hard cap: strong warning but NOT a block (user decides!)
103
228
  if [[ "$ACTIVE_COUNT" -ge "$HARD_CAP" ]]; then
104
- cat <<EOF
105
- {
106
- "decision": "approve",
107
- "systemMessage": "⚠️ WIP LIMIT EXCEEDED (${ACTIVE_COUNT}/${HARD_CAP})\n\nYou have $ACTIVE_COUNT active increments (configured maximum: $HARD_CAP)\n\nActive increments:\n$ACTIVE_LIST\n\n🧠 Research shows 3+ concurrent tasks = 40% slower + more bugs\n\n💡 Options:\n 1️⃣ Complete an increment: /specweave:done <id>\n 2️⃣ Pause an increment: /specweave:pause <id>\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\n 4️⃣ Continue anyway (not recommended)\n\n📝 To proceed anyway, just confirm your intent."
108
- }
109
- EOF
229
+ printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT EXCEEDED (%s/%s)\\n\\nYou have %s active increments (configured maximum: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Research shows 3+ concurrent tasks = 40%% slower + more bugs\\n\\n💡 Options:\\n 1️⃣ Complete an increment: /specweave:done <id>\\n 2️⃣ Pause an increment: /specweave:pause <id>\\n 3️⃣ Increase limit: Edit .specweave/config.json limits.hardCap\\n 4️⃣ Continue anyway (not recommended)\\n\\n📝 To proceed anyway, just confirm your intent."}\n' "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_COUNT" "$HARD_CAP" "$ACTIVE_LIST"
110
230
  exit 0
111
231
  fi
112
232
 
113
233
  # At soft limit: mild warning, approve
114
234
  if [[ "$ACTIVE_COUNT" -ge "$SOFT_LIMIT" ]]; then
115
- cat <<EOF
116
- {
117
- "decision": "approve",
118
- "systemMessage": "⚠️ WIP LIMIT REACHED (${ACTIVE_COUNT}/${SOFT_LIMIT})\n\nYou have $ACTIVE_COUNT active increment(s) (recommended limit: $SOFT_LIMIT)\n\nActive increments:\n$ACTIVE_LIST\n\n🧠 Focus Principle: Fewer active increments = maximum productivity\n\n💡 Consider:\n 1️⃣ Complete current work (recommended)\n 2️⃣ Pause current work (/specweave:pause)\n 3️⃣ Continue anyway\n\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"
119
- }
120
- EOF
235
+ printf '{"decision":"approve","systemMessage":"⚠️ WIP LIMIT REACHED (%s/%s)\\n\\nYou have %s active increment(s) (recommended limit: %s)\\n\\nActive increments:\\n%s\\n\\n🧠 Focus Principle: Fewer active increments = maximum productivity\\n\\n💡 Consider:\\n 1️⃣ Complete current work (recommended)\\n 2️⃣ Pause current work (/specweave:pause)\\n 3️⃣ Continue anyway\\n\\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug"}\n' "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_COUNT" "$SOFT_LIMIT" "$ACTIVE_LIST"
121
236
  exit 0
122
237
  fi
123
238
  fi
@@ -163,12 +278,7 @@ if [[ -n "$ACTIVE_INCREMENT" ]] && echo "$PROMPT" | grep -qE "/(specweave:)?(syn
163
278
  if [[ -f "$SPEC_FILE" ]] && [[ -f "$PLAN_FILE" ]]; then
164
279
  # Check if spec is newer than plan (indicates spec changes need sync)
165
280
  if [[ -n $(find "$SPEC_FILE" -newer "$PLAN_FILE" 2>/dev/null) ]]; then
166
- cat <<EOF
167
- {
168
- "decision": "approve",
169
- "systemMessage": "⚠️ Spec changes detected in $ACTIVE_INCREMENT\n\nspec.md has been modified after plan.md.\nConsider running /specweave:sync-docs to update living documentation."
170
- }
171
- EOF
281
+ printf '{"decision":"approve","systemMessage":"⚠️ Spec changes detected in %s\\n\\nspec.md has been modified after plan.md.\\nConsider running /specweave:sync-docs to update living documentation."}\n' "$ACTIVE_INCREMENT"
172
282
  exit 0
173
283
  fi
174
284
  fi
@@ -253,19 +363,11 @@ fi
253
363
  # ==============================================================================
254
364
 
255
365
  if [[ -n "$CONTEXT" ]]; then
256
- cat <<EOF
257
- {
258
- "decision": "approve",
259
- "systemMessage": "$CONTEXT"
260
- }
261
- EOF
366
+ # Escape context for JSON (newlines, quotes)
367
+ CONTEXT_ESCAPED=$(echo "$CONTEXT" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
368
+ printf '{"decision":"approve","systemMessage":"%s"}\n' "$CONTEXT_ESCAPED"
262
369
  else
263
- # Just approve, no extra context
264
- cat <<EOF
265
- {
266
- "decision": "approve"
267
- }
268
- EOF
370
+ echo '{"decision":"approve"}'
269
371
  fi
270
372
 
271
373
  exit 0