specweave 0.32.0 → 0.32.3

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 (347) hide show
  1. package/CLAUDE.md +215 -2
  2. package/README.md +22 -0
  3. package/bin/specweave.js +52 -1
  4. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts +100 -0
  5. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -0
  6. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +291 -0
  7. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -0
  8. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts +103 -0
  9. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -0
  10. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +310 -0
  11. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -0
  12. package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts +126 -0
  13. package/dist/plugins/specweave-jira/lib/jira-permission-gate.d.ts.map +1 -0
  14. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js +207 -0
  15. package/dist/plugins/specweave-jira/lib/jira-permission-gate.js.map +1 -0
  16. package/dist/src/adapters/codex/README.md +1 -1
  17. package/dist/src/adapters/codex/adapter.js +1 -1
  18. package/dist/src/cli/commands/archive.d.ts +2 -0
  19. package/dist/src/cli/commands/archive.d.ts.map +1 -1
  20. package/dist/src/cli/commands/archive.js +33 -0
  21. package/dist/src/cli/commands/archive.js.map +1 -1
  22. package/dist/src/cli/commands/cache.d.ts +17 -0
  23. package/dist/src/cli/commands/cache.d.ts.map +1 -0
  24. package/dist/src/cli/commands/cache.js +126 -0
  25. package/dist/src/cli/commands/cache.js.map +1 -0
  26. package/dist/src/cli/commands/context.d.ts +92 -0
  27. package/dist/src/cli/commands/context.d.ts.map +1 -0
  28. package/dist/src/cli/commands/context.js +205 -0
  29. package/dist/src/cli/commands/context.js.map +1 -0
  30. package/dist/src/cli/commands/init.d.ts.map +1 -1
  31. package/dist/src/cli/commands/init.js +112 -70
  32. package/dist/src/cli/commands/init.js.map +1 -1
  33. package/dist/src/cli/commands/plan/increment-detector.js +2 -2
  34. package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
  35. package/dist/src/cli/commands/sync-spec-commits.js +1 -1
  36. package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
  37. package/dist/src/cli/commands/sync-specs.js +2 -2
  38. package/dist/src/cli/commands/sync-specs.js.map +1 -1
  39. package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
  40. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  41. package/dist/src/cli/helpers/init/external-import.d.ts +3 -0
  42. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -1
  43. package/dist/src/cli/helpers/init/external-import.js +17 -4
  44. package/dist/src/cli/helpers/init/external-import.js.map +1 -1
  45. package/dist/src/cli/helpers/init/index.d.ts +1 -0
  46. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  47. package/dist/src/cli/helpers/init/index.js +2 -0
  48. package/dist/src/cli/helpers/init/index.js.map +1 -1
  49. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts +70 -0
  50. package/dist/src/cli/helpers/init/jira-ado-auto-detect.d.ts.map +1 -1
  51. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js +214 -4
  52. package/dist/src/cli/helpers/init/jira-ado-auto-detect.js.map +1 -1
  53. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts +4 -0
  54. package/dist/src/cli/helpers/init/living-docs-preflight.d.ts.map +1 -1
  55. package/dist/src/cli/helpers/init/living-docs-preflight.js +34 -3
  56. package/dist/src/cli/helpers/init/living-docs-preflight.js.map +1 -1
  57. package/dist/src/cli/helpers/init/testing-config.d.ts +3 -0
  58. package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -1
  59. package/dist/src/cli/helpers/init/testing-config.js +9 -2
  60. package/dist/src/cli/helpers/init/testing-config.js.map +1 -1
  61. package/dist/src/cli/helpers/init/translation-config.d.ts +3 -0
  62. package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -1
  63. package/dist/src/cli/helpers/init/translation-config.js +21 -4
  64. package/dist/src/cli/helpers/init/translation-config.js.map +1 -1
  65. package/dist/src/cli/helpers/init/wizard-navigation.d.ts +45 -0
  66. package/dist/src/cli/helpers/init/wizard-navigation.d.ts.map +1 -0
  67. package/dist/src/cli/helpers/init/wizard-navigation.js +97 -0
  68. package/dist/src/cli/helpers/init/wizard-navigation.js.map +1 -0
  69. package/dist/src/cli/workers/living-docs-worker.js +66 -1
  70. package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
  71. package/dist/src/config/types.d.ts +203 -1208
  72. package/dist/src/config/types.d.ts.map +1 -1
  73. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
  74. package/dist/src/core/discrepancy/increment-generator.js +5 -2
  75. package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
  76. package/dist/src/core/increment/duplicate-detector.js +2 -2
  77. package/dist/src/core/increment/duplicate-detector.js.map +1 -1
  78. package/dist/src/core/increment/increment-archiver.d.ts +49 -4
  79. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  80. package/dist/src/core/increment/increment-archiver.js +123 -22
  81. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  82. package/dist/src/core/increment/increment-status.js +2 -2
  83. package/dist/src/core/increment/increment-status.js.map +1 -1
  84. package/dist/src/core/increment/increment-utils.d.ts +150 -0
  85. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  86. package/dist/src/core/increment/increment-utils.js +216 -4
  87. package/dist/src/core/increment/increment-utils.js.map +1 -1
  88. package/dist/src/core/increment/metadata-validator.js +1 -1
  89. package/dist/src/core/increment/metadata-validator.js.map +1 -1
  90. package/dist/src/core/living-docs/feature-archiver.d.ts +4 -0
  91. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  92. package/dist/src/core/living-docs/feature-archiver.js +32 -10
  93. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  94. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
  95. package/dist/src/core/living-docs/feature-id-manager.js +8 -4
  96. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  97. package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts +38 -0
  98. package/dist/src/core/living-docs/governance/ecosystem-detector.d.ts.map +1 -0
  99. package/dist/src/core/living-docs/governance/ecosystem-detector.js +325 -0
  100. package/dist/src/core/living-docs/governance/ecosystem-detector.js.map +1 -0
  101. package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts +74 -0
  102. package/dist/src/core/living-docs/governance/frontend-standards-parser.d.ts.map +1 -0
  103. package/dist/src/core/living-docs/governance/frontend-standards-parser.js +366 -0
  104. package/dist/src/core/living-docs/governance/frontend-standards-parser.js.map +1 -0
  105. package/dist/src/core/living-docs/governance/go-standards-parser.d.ts +64 -0
  106. package/dist/src/core/living-docs/governance/go-standards-parser.d.ts.map +1 -0
  107. package/dist/src/core/living-docs/governance/go-standards-parser.js +229 -0
  108. package/dist/src/core/living-docs/governance/go-standards-parser.js.map +1 -0
  109. package/dist/src/core/living-docs/governance/index.d.ts +50 -0
  110. package/dist/src/core/living-docs/governance/index.d.ts.map +1 -0
  111. package/dist/src/core/living-docs/governance/index.js +56 -0
  112. package/dist/src/core/living-docs/governance/index.js.map +1 -0
  113. package/dist/src/core/living-docs/governance/java-standards-parser.d.ts +89 -0
  114. package/dist/src/core/living-docs/governance/java-standards-parser.d.ts.map +1 -0
  115. package/dist/src/core/living-docs/governance/java-standards-parser.js +356 -0
  116. package/dist/src/core/living-docs/governance/java-standards-parser.js.map +1 -0
  117. package/dist/src/core/living-docs/governance/python-standards-parser.d.ts +83 -0
  118. package/dist/src/core/living-docs/governance/python-standards-parser.d.ts.map +1 -0
  119. package/dist/src/core/living-docs/governance/python-standards-parser.js +347 -0
  120. package/dist/src/core/living-docs/governance/python-standards-parser.js.map +1 -0
  121. package/dist/src/core/living-docs/governance/standards-generator.d.ts +38 -0
  122. package/dist/src/core/living-docs/governance/standards-generator.d.ts.map +1 -0
  123. package/dist/src/core/living-docs/governance/standards-generator.js +476 -0
  124. package/dist/src/core/living-docs/governance/standards-generator.js.map +1 -0
  125. package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
  126. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  127. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
  128. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
  129. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +299 -0
  130. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
  131. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
  132. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
  133. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
  134. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
  135. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
  136. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
  137. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
  138. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
  139. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
  140. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
  141. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
  142. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
  143. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
  144. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
  145. package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
  146. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
  147. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +22 -0
  148. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
  149. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +482 -0
  150. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
  151. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
  152. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
  153. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
  154. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
  155. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +190 -0
  156. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
  157. package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
  158. package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
  159. package/dist/src/core/living-docs/living-docs-sync.d.ts +11 -3
  160. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  161. package/dist/src/core/living-docs/living-docs-sync.js +53 -10
  162. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  163. package/dist/src/core/living-docs/module-analyzer.d.ts +22 -0
  164. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
  165. package/dist/src/core/living-docs/module-analyzer.js +123 -19
  166. package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
  167. package/dist/src/core/llm/provider-factory.js +2 -2
  168. package/dist/src/core/llm/provider-factory.js.map +1 -1
  169. package/dist/src/core/llm/providers/anthropic-provider.js +1 -1
  170. package/dist/src/core/llm/providers/bedrock-provider.d.ts.map +1 -1
  171. package/dist/src/core/llm/providers/bedrock-provider.js +8 -4
  172. package/dist/src/core/llm/providers/bedrock-provider.js.map +1 -1
  173. package/dist/src/core/sync/spec-increment-mapper.js +3 -3
  174. package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
  175. package/dist/src/importers/item-converter.d.ts +25 -0
  176. package/dist/src/importers/item-converter.d.ts.map +1 -1
  177. package/dist/src/importers/item-converter.js +135 -5
  178. package/dist/src/importers/item-converter.js.map +1 -1
  179. package/dist/src/importers/jira-importer.d.ts +14 -0
  180. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  181. package/dist/src/importers/jira-importer.js +75 -0
  182. package/dist/src/importers/jira-importer.js.map +1 -1
  183. package/dist/src/init/architecture/types.d.ts +33 -140
  184. package/dist/src/init/architecture/types.d.ts.map +1 -1
  185. package/dist/src/init/compliance/types.d.ts +30 -27
  186. package/dist/src/init/compliance/types.d.ts.map +1 -1
  187. package/dist/src/init/repo/types.d.ts +11 -34
  188. package/dist/src/init/repo/types.d.ts.map +1 -1
  189. package/dist/src/init/research/src/config/types.d.ts +15 -82
  190. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  191. package/dist/src/init/research/types.d.ts +38 -93
  192. package/dist/src/init/research/types.d.ts.map +1 -1
  193. package/dist/src/init/team/types.d.ts +4 -42
  194. package/dist/src/init/team/types.d.ts.map +1 -1
  195. package/dist/src/integrations/jira/jira-token-provider.d.ts +93 -0
  196. package/dist/src/integrations/jira/jira-token-provider.d.ts.map +1 -0
  197. package/dist/src/integrations/jira/jira-token-provider.js +160 -0
  198. package/dist/src/integrations/jira/jira-token-provider.js.map +1 -0
  199. package/dist/src/sync/ado-reconciler.d.ts +92 -0
  200. package/dist/src/sync/ado-reconciler.d.ts.map +1 -0
  201. package/dist/src/sync/ado-reconciler.js +335 -0
  202. package/dist/src/sync/ado-reconciler.js.map +1 -0
  203. package/dist/src/sync/jira-reconciler.d.ts +106 -0
  204. package/dist/src/sync/jira-reconciler.d.ts.map +1 -0
  205. package/dist/src/sync/jira-reconciler.js +405 -0
  206. package/dist/src/sync/jira-reconciler.js.map +1 -0
  207. package/dist/src/types/dashboard-cache.d.ts +181 -0
  208. package/dist/src/types/dashboard-cache.d.ts.map +1 -0
  209. package/dist/src/types/dashboard-cache.js +65 -0
  210. package/dist/src/types/dashboard-cache.js.map +1 -0
  211. package/dist/src/types/model-selection.d.ts +6 -4
  212. package/dist/src/types/model-selection.d.ts.map +1 -1
  213. package/dist/src/types/model-selection.js +3 -1
  214. package/dist/src/types/model-selection.js.map +1 -1
  215. package/dist/src/utils/docs-validator.d.ts +131 -0
  216. package/dist/src/utils/docs-validator.d.ts.map +1 -0
  217. package/dist/src/utils/docs-validator.js +529 -0
  218. package/dist/src/utils/docs-validator.js.map +1 -0
  219. package/dist/src/utils/external-tool-drift-detector.d.ts +1 -1
  220. package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -1
  221. package/dist/src/utils/external-tool-drift-detector.js +5 -4
  222. package/dist/src/utils/external-tool-drift-detector.js.map +1 -1
  223. package/dist/src/utils/feature-id-collision.js +1 -1
  224. package/dist/src/utils/feature-id-collision.js.map +1 -1
  225. package/dist/src/utils/feature-id-derivation.d.ts +8 -3
  226. package/dist/src/utils/feature-id-derivation.d.ts.map +1 -1
  227. package/dist/src/utils/feature-id-derivation.js +14 -6
  228. package/dist/src/utils/feature-id-derivation.js.map +1 -1
  229. package/dist/src/utils/html-to-mdx.d.ts +1 -0
  230. package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
  231. package/dist/src/utils/html-to-mdx.js +43 -5
  232. package/dist/src/utils/html-to-mdx.js.map +1 -1
  233. package/dist/src/utils/model-selection.d.ts +3 -4
  234. package/dist/src/utils/model-selection.d.ts.map +1 -1
  235. package/dist/src/utils/model-selection.js +3 -4
  236. package/dist/src/utils/model-selection.js.map +1 -1
  237. package/package.json +1 -1
  238. package/plugins/specweave/agents/code-standards-detective/AGENT.md +48 -0
  239. package/plugins/specweave/agents/pm/AGENT.md +10 -7
  240. package/plugins/specweave/commands/specweave-archive-features.md +5 -7
  241. package/plugins/specweave/commands/specweave-archive.md +2 -1
  242. package/plugins/specweave/commands/specweave-costs.md +4 -4
  243. package/plugins/specweave/commands/specweave-do.md +44 -10
  244. package/plugins/specweave/commands/specweave-done.md +109 -0
  245. package/plugins/specweave/commands/specweave-import-external.md +45 -18
  246. package/plugins/specweave/commands/specweave-increment.md +331 -33
  247. package/plugins/specweave/commands/specweave-jobs.md +2 -2
  248. package/plugins/specweave/commands/specweave-progress.md +4 -4
  249. package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
  250. package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
  251. package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
  252. package/plugins/specweave/commands/specweave-validate-features.md +13 -8
  253. package/plugins/specweave/commands/specweave-validate.md +27 -1
  254. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  255. package/plugins/specweave/hooks/hooks.json +43 -4
  256. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  257. package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
  258. package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
  259. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  260. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  261. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  262. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  263. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  264. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  265. package/plugins/specweave/hooks/post-task-completion.sh +4 -23
  266. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  267. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
  268. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  269. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  270. package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
  271. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  272. package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
  273. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  274. package/plugins/specweave/hooks/spec-project-validator.sh +80 -25
  275. package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
  276. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
  277. package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
  278. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  279. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
  280. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
  281. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
  282. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
  283. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
  284. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
  285. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +135 -0
  286. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
  287. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
  288. package/plugins/specweave/scripts/README.md +166 -0
  289. package/plugins/specweave/scripts/cleanup-state.sh +142 -0
  290. package/plugins/specweave/scripts/force-kill.sh +142 -0
  291. package/plugins/specweave/scripts/jobs.js +171 -0
  292. package/plugins/specweave/scripts/progress.js +170 -0
  293. package/plugins/specweave/scripts/read-costs.sh +132 -0
  294. package/plugins/specweave/scripts/read-jobs.sh +324 -0
  295. package/plugins/specweave/scripts/read-progress.sh +185 -0
  296. package/plugins/specweave/scripts/read-status.sh +146 -0
  297. package/plugins/specweave/scripts/read-workflow.sh +173 -0
  298. package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
  299. package/plugins/specweave/scripts/session-watchdog.sh +192 -0
  300. package/plugins/specweave/scripts/status.js +154 -0
  301. package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
  302. package/plugins/specweave/skills/code-standards-analyzer/SKILL.md +58 -6
  303. package/plugins/specweave/skills/increment-planner/SKILL.md +388 -48
  304. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -7
  305. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -1
  306. package/plugins/specweave/skills/increment-planner/templates/tasks-multi-project.md +1 -1
  307. package/plugins/specweave/skills/increment-planner/templates/tasks-single-project.md +1 -1
  308. package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
  309. package/plugins/specweave-ado/commands/cleanup-duplicates.md +212 -0
  310. package/plugins/specweave-ado/commands/reconcile.md +120 -0
  311. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  312. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  313. package/plugins/specweave-ado/lib/ado-duplicate-detector.js +279 -0
  314. package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +407 -0
  315. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  316. package/plugins/specweave-docs/commands/build.md +32 -4
  317. package/plugins/specweave-docs/commands/preview.md +43 -1
  318. package/plugins/specweave-docs/commands/validate.md +250 -0
  319. package/plugins/specweave-github/agents/github-manager/AGENT.md +2 -2
  320. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -0
  321. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  322. package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
  323. package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
  324. package/plugins/specweave-jira/agents/jira-manager/AGENT.md +1 -1
  325. package/plugins/specweave-jira/agents/jira-multi-project-mapper/AGENT.md +530 -0
  326. package/plugins/specweave-jira/agents/jira-sync-judge/AGENT.md +438 -0
  327. package/plugins/specweave-jira/commands/cleanup-duplicates.md +219 -0
  328. package/plugins/specweave-jira/commands/close.md +297 -0
  329. package/plugins/specweave-jira/commands/create.md +198 -0
  330. package/plugins/specweave-jira/commands/reconcile.md +123 -0
  331. package/plugins/specweave-jira/commands/status.md +215 -0
  332. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  333. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  334. package/plugins/specweave-jira/lib/jira-duplicate-detector.js +296 -0
  335. package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +434 -0
  336. package/plugins/specweave-jira/lib/jira-permission-gate.js +160 -0
  337. package/plugins/specweave-jira/lib/jira-permission-gate.ts +276 -0
  338. package/plugins/specweave-jira/lib/jira-profile-resolver.js +222 -0
  339. package/plugins/specweave-jira/lib/jira-profile-resolver.ts +427 -0
  340. package/plugins/specweave-jira/reference/jira-specweave-mapping.md +16 -11
  341. package/plugins/specweave-release/commands/specweave-release-npm.md +140 -14
  342. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -0
  343. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  344. package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
  345. package/plugins/specweave/hooks/post-write-spec.sh +0 -267
  346. package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
  347. package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
@@ -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
  }
@@ -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,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