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
@@ -0,0 +1,407 @@
1
+ /**
2
+ * Azure DevOps Duplicate Detector (v0.33.0)
3
+ *
4
+ * Implements 3-phase duplicate protection for ADO work items:
5
+ * 1. Detection: Check before create
6
+ * 2. Verification: Count check after create
7
+ * 3. Reflection: Auto-close duplicates
8
+ *
9
+ * Mirrors the GitHub DuplicateDetector pattern for consistency.
10
+ */
11
+
12
+ import { Logger, consoleLogger } from '../../../src/utils/logger.js';
13
+
14
+ export interface AdoWorkItem {
15
+ id: number;
16
+ title: string;
17
+ state: string;
18
+ createdDate: string;
19
+ url?: string;
20
+ }
21
+
22
+ export interface DuplicateGroup {
23
+ title: string;
24
+ items: AdoWorkItem[];
25
+ keepItem: AdoWorkItem;
26
+ duplicates: AdoWorkItem[];
27
+ }
28
+
29
+ export interface DetectionResult {
30
+ found: boolean;
31
+ existingItem?: AdoWorkItem;
32
+ count: number;
33
+ }
34
+
35
+ export interface VerificationResult {
36
+ success: boolean;
37
+ expectedCount: number;
38
+ actualCount: number;
39
+ duplicates: AdoWorkItem[];
40
+ }
41
+
42
+ export interface CleanupResult {
43
+ closedCount: number;
44
+ keptCount: number;
45
+ errors: string[];
46
+ }
47
+
48
+ export class AdoDuplicateDetector {
49
+ private org: string;
50
+ private project: string;
51
+ private pat: string;
52
+ private logger: Logger;
53
+
54
+ constructor(options: {
55
+ org?: string;
56
+ project?: string;
57
+ pat?: string;
58
+ logger?: Logger;
59
+ } = {}) {
60
+ this.org = options.org || process.env.AZURE_DEVOPS_ORG || '';
61
+ this.project = options.project || process.env.AZURE_DEVOPS_PROJECT || '';
62
+ this.pat = options.pat || process.env.AZURE_DEVOPS_PAT || process.env.AZURE_DEVOPS_TOKEN || '';
63
+ this.logger = options.logger || consoleLogger;
64
+ }
65
+
66
+ /**
67
+ * Phase 1: Check if work item exists before creating
68
+ */
69
+ async checkBeforeCreate(
70
+ titlePattern: string,
71
+ incrementId?: string
72
+ ): Promise<DetectionResult> {
73
+ try {
74
+ const items = await this.searchWorkItems(titlePattern);
75
+
76
+ if (items.length > 0) {
77
+ return {
78
+ found: true,
79
+ existingItem: items[0],
80
+ count: items.length,
81
+ };
82
+ }
83
+
84
+ return { found: false, count: 0 };
85
+ } catch (error: any) {
86
+ this.logger.log(`āš ļø Detection check failed: ${error.message}`);
87
+ // Graceful degradation - continue anyway
88
+ return { found: false, count: 0 };
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Phase 2: Verify count after creation
94
+ */
95
+ async verifyAfterCreate(
96
+ titlePattern: string,
97
+ expectedCount: number = 1
98
+ ): Promise<VerificationResult> {
99
+ try {
100
+ const items = await this.searchWorkItems(titlePattern);
101
+
102
+ if (items.length > expectedCount) {
103
+ // Duplicates detected!
104
+ const sorted = items.sort(
105
+ (a, b) => a.id - b.id // Sort by ID (lowest = oldest)
106
+ );
107
+
108
+ return {
109
+ success: false,
110
+ expectedCount,
111
+ actualCount: items.length,
112
+ duplicates: sorted.slice(expectedCount), // All items after expected count
113
+ };
114
+ }
115
+
116
+ return {
117
+ success: true,
118
+ expectedCount,
119
+ actualCount: items.length,
120
+ duplicates: [],
121
+ };
122
+ } catch (error: any) {
123
+ this.logger.log(`āš ļø Verification check failed: ${error.message}`);
124
+ return {
125
+ success: true, // Assume success on error
126
+ expectedCount,
127
+ actualCount: expectedCount,
128
+ duplicates: [],
129
+ };
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Phase 3: Auto-close duplicates
135
+ */
136
+ async closeDuplicates(
137
+ duplicates: AdoWorkItem[],
138
+ keepItemId: number
139
+ ): Promise<CleanupResult> {
140
+ const result: CleanupResult = {
141
+ closedCount: 0,
142
+ keptCount: 1,
143
+ errors: [],
144
+ };
145
+
146
+ for (const item of duplicates) {
147
+ try {
148
+ await this.closeWorkItem(item.id, keepItemId);
149
+ result.closedCount++;
150
+ this.logger.log(` āœ… Closed #${item.id} (duplicate of #${keepItemId})`);
151
+ } catch (error: any) {
152
+ result.errors.push(`#${item.id}: ${error.message}`);
153
+ this.logger.log(` āŒ Failed to close #${item.id}: ${error.message}`);
154
+ }
155
+ }
156
+
157
+ return result;
158
+ }
159
+
160
+ /**
161
+ * Full cleanup: Find and close all duplicates for a feature
162
+ */
163
+ async cleanupFeatureDuplicates(
164
+ featureId: string,
165
+ dryRun: boolean = false
166
+ ): Promise<{
167
+ groups: DuplicateGroup[];
168
+ totalItems: number;
169
+ duplicateCount: number;
170
+ closedCount: number;
171
+ }> {
172
+ // 1. Search for all work items with feature ID
173
+ const searchPattern = `[${featureId}]`;
174
+ const items = await this.searchWorkItems(searchPattern);
175
+
176
+ this.logger.log(`\nšŸ” Scanning for duplicates in Feature ${featureId}...`);
177
+ this.logger.log(` Found ${items.length} total work items`);
178
+
179
+ // 2. Group by title
180
+ const groups = this.groupByTitle(items);
181
+ const duplicateGroups = groups.filter(g => g.duplicates.length > 0);
182
+
183
+ if (duplicateGroups.length === 0) {
184
+ this.logger.log(` āœ… No duplicates found!`);
185
+ return {
186
+ groups: [],
187
+ totalItems: items.length,
188
+ duplicateCount: 0,
189
+ closedCount: 0,
190
+ };
191
+ }
192
+
193
+ this.logger.log(` Detected ${duplicateGroups.length} duplicate groups:\n`);
194
+
195
+ // 3. Display groups
196
+ for (let i = 0; i < duplicateGroups.length; i++) {
197
+ const group = duplicateGroups[i];
198
+ this.logger.log(` šŸ“‹ Group ${i + 1}: "${group.title.substring(0, 50)}..."`);
199
+ this.logger.log(` - #${group.keepItem.id} (KEEP) - Created ${group.keepItem.createdDate.split('T')[0]}`);
200
+ for (const dup of group.duplicates) {
201
+ this.logger.log(` - #${dup.id} (CLOSE) - Created ${dup.createdDate.split('T')[0]} - DUPLICATE`);
202
+ }
203
+ this.logger.log('');
204
+ }
205
+
206
+ const totalDuplicates = duplicateGroups.reduce((sum, g) => sum + g.duplicates.length, 0);
207
+
208
+ if (dryRun) {
209
+ this.logger.log(`\nāœ… Dry run complete!`);
210
+ this.logger.log(` Total work items: ${items.length}`);
211
+ this.logger.log(` Duplicate groups: ${duplicateGroups.length}`);
212
+ this.logger.log(` Work items to close: ${totalDuplicates}`);
213
+ this.logger.log(`\nāš ļø This was a DRY RUN - no changes made.`);
214
+
215
+ return {
216
+ groups: duplicateGroups,
217
+ totalItems: items.length,
218
+ duplicateCount: totalDuplicates,
219
+ closedCount: 0,
220
+ };
221
+ }
222
+
223
+ // 4. Close duplicates
224
+ let closedCount = 0;
225
+ this.logger.log(`šŸ—‘ļø Closing duplicates...`);
226
+
227
+ for (const group of duplicateGroups) {
228
+ const result = await this.closeDuplicates(group.duplicates, group.keepItem.id);
229
+ closedCount += result.closedCount;
230
+ }
231
+
232
+ this.logger.log(`\nāœ… Cleanup complete!`);
233
+ this.logger.log(` Closed: ${closedCount} duplicates`);
234
+ this.logger.log(` Kept: ${duplicateGroups.length} original work items`);
235
+
236
+ return {
237
+ groups: duplicateGroups,
238
+ totalItems: items.length,
239
+ duplicateCount: totalDuplicates,
240
+ closedCount,
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Group work items by title
246
+ */
247
+ private groupByTitle(items: AdoWorkItem[]): DuplicateGroup[] {
248
+ const titleMap = new Map<string, AdoWorkItem[]>();
249
+
250
+ for (const item of items) {
251
+ const existing = titleMap.get(item.title) || [];
252
+ existing.push(item);
253
+ titleMap.set(item.title, existing);
254
+ }
255
+
256
+ const groups: DuplicateGroup[] = [];
257
+
258
+ for (const [title, groupItems] of titleMap) {
259
+ // Sort by ID (lowest = oldest)
260
+ const sorted = groupItems.sort((a, b) => a.id - b.id);
261
+
262
+ groups.push({
263
+ title,
264
+ items: sorted,
265
+ keepItem: sorted[0],
266
+ duplicates: sorted.slice(1),
267
+ });
268
+ }
269
+
270
+ return groups;
271
+ }
272
+
273
+ /**
274
+ * Search for work items using WIQL
275
+ */
276
+ private async searchWorkItems(titlePattern: string): Promise<AdoWorkItem[]> {
277
+ if (!this.org || !this.pat) {
278
+ throw new Error('ADO credentials not configured');
279
+ }
280
+
281
+ const wiql = {
282
+ query: `SELECT [System.Id], [System.Title], [System.State], [System.CreatedDate]
283
+ FROM WorkItems
284
+ WHERE [System.TeamProject] = '${this.project}'
285
+ AND [System.Title] CONTAINS '${titlePattern}'
286
+ ORDER BY [System.Id] ASC`,
287
+ };
288
+
289
+ const auth = Buffer.from(`:${this.pat}`).toString('base64');
290
+ const url = `https://dev.azure.com/${this.org}/${this.project}/_apis/wit/wiql?api-version=7.1`;
291
+
292
+ const response = await fetch(url, {
293
+ method: 'POST',
294
+ headers: {
295
+ Authorization: `Basic ${auth}`,
296
+ 'Content-Type': 'application/json',
297
+ },
298
+ body: JSON.stringify(wiql),
299
+ });
300
+
301
+ if (!response.ok) {
302
+ throw new Error(`WIQL query failed: ${response.status}`);
303
+ }
304
+
305
+ const data = await response.json();
306
+ const workItemIds = data.workItems?.map((wi: any) => wi.id) || [];
307
+
308
+ if (workItemIds.length === 0) {
309
+ return [];
310
+ }
311
+
312
+ // Fetch full work item details
313
+ return this.getWorkItemDetails(workItemIds);
314
+ }
315
+
316
+ /**
317
+ * Get work item details by IDs
318
+ */
319
+ private async getWorkItemDetails(ids: number[]): Promise<AdoWorkItem[]> {
320
+ if (ids.length === 0) return [];
321
+
322
+ const auth = Buffer.from(`:${this.pat}`).toString('base64');
323
+ const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems?ids=${ids.join(',')}&api-version=7.1`;
324
+
325
+ const response = await fetch(url, {
326
+ headers: {
327
+ Authorization: `Basic ${auth}`,
328
+ 'Content-Type': 'application/json',
329
+ },
330
+ });
331
+
332
+ if (!response.ok) {
333
+ throw new Error(`Failed to get work items: ${response.status}`);
334
+ }
335
+
336
+ const data = await response.json();
337
+ return (data.value || []).map((wi: any) => ({
338
+ id: wi.id,
339
+ title: wi.fields['System.Title'],
340
+ state: wi.fields['System.State'],
341
+ createdDate: wi.fields['System.CreatedDate'],
342
+ url: wi._links?.html?.href,
343
+ }));
344
+ }
345
+
346
+ /**
347
+ * Close a work item with duplicate comment
348
+ */
349
+ private async closeWorkItem(workItemId: number, originalId: number): Promise<void> {
350
+ const auth = Buffer.from(`:${this.pat}`).toString('base64');
351
+ const url = `https://dev.azure.com/${this.org}/_apis/wit/workitems/${workItemId}?api-version=7.1`;
352
+
353
+ const comment = `## Duplicate of #${originalId}
354
+
355
+ This work item was automatically closed by SpecWeave cleanup because it is a duplicate.
356
+
357
+ The original work item (#${originalId}) contains the same content and should be used for tracking instead.
358
+
359
+ ---
360
+ šŸ¤– Auto-closed by SpecWeave Duplicate Cleanup`;
361
+
362
+ const operations = [
363
+ {
364
+ op: 'add',
365
+ path: '/fields/System.State',
366
+ value: 'Closed',
367
+ },
368
+ {
369
+ op: 'add',
370
+ path: '/fields/System.History',
371
+ value: comment,
372
+ },
373
+ ];
374
+
375
+ const response = await fetch(url, {
376
+ method: 'PATCH',
377
+ headers: {
378
+ Authorization: `Basic ${auth}`,
379
+ 'Content-Type': 'application/json-patch+json',
380
+ },
381
+ body: JSON.stringify(operations),
382
+ });
383
+
384
+ if (!response.ok) {
385
+ const error = await response.text();
386
+ throw new Error(`Failed to close work item: ${response.status} - ${error}`);
387
+ }
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Convenience function for quick duplicate cleanup
393
+ */
394
+ export async function cleanupAdoDuplicates(
395
+ featureId: string,
396
+ dryRun: boolean = false
397
+ ): Promise<{
398
+ groups: DuplicateGroup[];
399
+ totalItems: number;
400
+ duplicateCount: number;
401
+ closedCount: number;
402
+ }> {
403
+ const detector = new AdoDuplicateDetector();
404
+ return detector.cleanupFeatureDuplicates(featureId, dryRun);
405
+ }
406
+
407
+ export default AdoDuplicateDetector;
@@ -0,0 +1,170 @@
1
+ import { AdoClientV2 } from "./ado-client-v2.js";
2
+ import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
3
+ import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
4
+ import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
5
+ import path from "path";
6
+ import fs from "fs/promises";
7
+ async function syncSpecToAdoWithEnhancedContent(options) {
8
+ const { specPath, organization, project, dryRun = false, verbose = false } = options;
9
+ try {
10
+ const baseSpec = await parseSpecContent(specPath);
11
+ if (!baseSpec) {
12
+ return {
13
+ success: false,
14
+ action: "error",
15
+ error: "Failed to parse spec content"
16
+ };
17
+ }
18
+ if (verbose) {
19
+ console.log(`\u{1F4C4} Parsed spec: ${baseSpec.identifier.compact}`);
20
+ }
21
+ const specId = baseSpec.identifier.full || baseSpec.identifier.compact;
22
+ const rootDir = await findSpecWeaveRoot(specPath);
23
+ const mapper = new SpecIncrementMapper(rootDir);
24
+ const mapping = await mapper.mapSpecToIncrements(specId);
25
+ if (verbose) {
26
+ console.log(`\u{1F517} Found ${mapping.increments.length} related increments`);
27
+ }
28
+ const taskMapping = buildTaskMapping(mapping.increments, organization, project);
29
+ const architectureDocs = await findArchitectureDocs(rootDir, specId);
30
+ const enhancedSpec = {
31
+ ...baseSpec,
32
+ summary: baseSpec.description,
33
+ taskMapping,
34
+ architectureDocs
35
+ };
36
+ const builder = new EnhancedContentBuilder();
37
+ const description = builder.buildExternalDescription(enhancedSpec);
38
+ if (verbose) {
39
+ console.log(`\u{1F4DD} Generated description: ${description.length} characters`);
40
+ }
41
+ if (dryRun) {
42
+ console.log("\u{1F50D} DRY RUN - Would create/update feature with:");
43
+ console.log(` Title: ${baseSpec.title}`);
44
+ console.log(` Description length: ${description.length}`);
45
+ return {
46
+ success: true,
47
+ action: "no-change",
48
+ tasksLinked: taskMapping?.tasks.length || 0
49
+ };
50
+ }
51
+ if (!organization || !project) {
52
+ return {
53
+ success: false,
54
+ action: "error",
55
+ error: "Azure DevOps organization/project not specified"
56
+ };
57
+ }
58
+ const profile = {
59
+ provider: "ado",
60
+ displayName: `${organization}/${project}`,
61
+ config: {
62
+ organization,
63
+ project
64
+ },
65
+ timeRange: { default: "1M", max: "6M" }
66
+ };
67
+ const pat = process.env.AZURE_DEVOPS_PAT || "";
68
+ const client = new AdoClientV2(profile, pat);
69
+ const existingFeature = await findExistingFeature(client, baseSpec.identifier.compact);
70
+ let result;
71
+ if (existingFeature) {
72
+ await client.updateWorkItem(existingFeature.id, {
73
+ title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
74
+ description
75
+ });
76
+ result = {
77
+ success: true,
78
+ action: "updated",
79
+ featureId: existingFeature.id,
80
+ featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${existingFeature.id}`,
81
+ tasksLinked: taskMapping?.tasks.length || 0
82
+ };
83
+ } else {
84
+ const feature = await client.createEpic({
85
+ title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
86
+ description,
87
+ tags: ["spec", "external-tool-sync"]
88
+ });
89
+ result = {
90
+ success: true,
91
+ action: "created",
92
+ featureId: feature.id,
93
+ featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${feature.id}`,
94
+ tasksLinked: taskMapping?.tasks.length || 0
95
+ };
96
+ }
97
+ if (verbose) {
98
+ console.log(`\u2705 ${result.action === "created" ? "Created" : "Updated"} feature #${result.featureId}`);
99
+ }
100
+ return result;
101
+ } catch (error) {
102
+ return {
103
+ success: false,
104
+ action: "error",
105
+ error: error.message
106
+ };
107
+ }
108
+ }
109
+ async function findSpecWeaveRoot(specPath) {
110
+ let currentDir = path.dirname(specPath);
111
+ while (true) {
112
+ const specweaveDir = path.join(currentDir, ".specweave");
113
+ try {
114
+ await fs.access(specweaveDir);
115
+ return currentDir;
116
+ } catch {
117
+ const parentDir = path.dirname(currentDir);
118
+ if (parentDir === currentDir) {
119
+ throw new Error(".specweave directory not found");
120
+ }
121
+ currentDir = parentDir;
122
+ }
123
+ }
124
+ }
125
+ function buildTaskMapping(increments, organization, project) {
126
+ if (increments.length === 0) return void 0;
127
+ const firstIncrement = increments[0];
128
+ const tasks = firstIncrement.tasks.map((task) => ({
129
+ id: task.id,
130
+ title: task.title,
131
+ userStories: task.userStories
132
+ }));
133
+ return {
134
+ incrementId: firstIncrement.id,
135
+ tasks,
136
+ tasksUrl: `https://dev.azure.com/${organization}/${project}/_git/repo?path=/.specweave/increments/${firstIncrement.id}/tasks.md`
137
+ };
138
+ }
139
+ async function findArchitectureDocs(rootDir, specId) {
140
+ const docs = [];
141
+ const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
142
+ try {
143
+ const adrDir = path.join(archDir, "adr");
144
+ try {
145
+ const adrs = await fs.readdir(adrDir);
146
+ const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
147
+ for (const adr of relatedAdrs) {
148
+ docs.push({
149
+ type: "adr",
150
+ path: path.join(adrDir, adr),
151
+ title: adr.replace(".md", "").replace(/-/g, " ")
152
+ });
153
+ }
154
+ } catch {
155
+ }
156
+ } catch {
157
+ }
158
+ return docs;
159
+ }
160
+ async function findExistingFeature(client, specId) {
161
+ try {
162
+ const features = await client.queryWorkItems(`[System.Title] Contains '[${specId}]' AND [System.WorkItemType] = 'Feature'`);
163
+ return features[0] || null;
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+ export {
169
+ syncSpecToAdoWithEnhancedContent
170
+ };
@@ -1,19 +1,47 @@
1
1
  ---
2
2
  name: specweave-docs:build
3
- description: Build static documentation site for deployment. Auto-setup on first run. Outputs production-ready HTML/CSS/JS.
3
+ description: Build static documentation site for deployment. Validates docs first, auto-fixes issues, auto-setup on first run. Outputs production-ready HTML/CSS/JS.
4
4
  ---
5
5
 
6
6
  # Documentation Build Command
7
7
 
8
8
  Build production-ready static documentation site for deployment to any static host.
9
9
 
10
+ **CRITICAL**: Runs pre-flight validation to catch issues BEFORE building.
11
+
10
12
  ## Your Task
11
13
 
12
14
  **IMPORTANT**: This command must work in ANY SpecWeave user project, not just the SpecWeave repo itself.
13
15
 
14
- ### Step 1: Ensure Docusaurus is Set Up
16
+ ### Step 1: CRITICAL - Run Pre-Flight Validation
17
+
18
+ **ALWAYS validate BEFORE building to prevent cryptic webpack errors!**
19
+
20
+ ```typescript
21
+ import { DocsValidator } from '../../../src/utils/docs-validator.js';
22
+
23
+ const validator = new DocsValidator({
24
+ docsPath: '.specweave/docs/internal',
25
+ autoFix: true, // Auto-fix common issues
26
+ });
27
+
28
+ console.log('\nšŸ” Running pre-build validation...\n');
29
+ const result = await validator.validate();
30
+
31
+ // Show summary
32
+ console.log(DocsValidator.formatResult(result));
33
+
34
+ // If errors remain after auto-fix, STOP and report
35
+ if (!result.valid) {
36
+ console.log('\nāŒ Documentation has errors that must be fixed before build.');
37
+ console.log(' Fix the issues above, then try again.\n');
38
+ process.exit(1);
39
+ }
40
+
41
+ console.log('\nāœ… Validation passed! Proceeding with build...\n');
42
+ ```
15
43
 
16
- First, ensure the cached Docusaurus installation exists:
44
+ ### Step 2: Ensure Docusaurus is Set Up
17
45
 
18
46
  ```bash
19
47
  # Check if Docusaurus is set up
@@ -26,7 +54,7 @@ fi
26
54
 
27
55
  If not set up, follow the same setup steps as `/specweave-docs:preview` (Step 3 in preview.md).
28
56
 
29
- ### Step 2: Run Build
57
+ ### Step 3: Run Build
30
58
 
31
59
  ```bash
32
60
  cd .specweave/cache/docs-site && npm run build
@@ -1,12 +1,14 @@
1
1
  ---
2
2
  name: specweave-docs:preview
3
- description: Launch Docusaurus documentation server for internal living docs. Auto-setup on first run. Port 3015.
3
+ description: Launch Docusaurus documentation server for internal living docs. Validates docs first, auto-fixes issues, auto-setup on first run. Port 3015.
4
4
  ---
5
5
 
6
6
  # Documentation Preview Command
7
7
 
8
8
  Launch Docusaurus development server with hot reload, Mermaid diagrams, and auto-generated sidebar.
9
9
 
10
+ **CRITICAL**: Runs pre-flight validation to catch issues BEFORE starting the server.
11
+
10
12
  ## Your Task
11
13
 
12
14
  **IMPORTANT**: This command must work in ANY SpecWeave user project, not just the SpecWeave repo itself.
@@ -22,6 +24,46 @@ ls -la .specweave/docs/internal/
22
24
  # Run 'specweave init' first or create the folder structure."
23
25
  ```
24
26
 
27
+ ### Step 1.5: CRITICAL - Run Pre-Flight Validation
28
+
29
+ **ALWAYS run validation BEFORE starting the server!**
30
+
31
+ ```typescript
32
+ import { DocsValidator } from '../../../src/utils/docs-validator.js';
33
+
34
+ const validator = new DocsValidator({
35
+ docsPath: '.specweave/docs/internal',
36
+ autoFix: true, // Auto-fix common issues
37
+ });
38
+
39
+ console.log('\nšŸ” Running pre-flight validation...\n');
40
+ const result = await validator.validate();
41
+
42
+ // Show summary
43
+ console.log(DocsValidator.formatResult(result));
44
+
45
+ // If errors remain after auto-fix, STOP and report
46
+ if (!result.valid) {
47
+ console.log('\nāŒ Documentation has errors that must be fixed before preview.');
48
+ console.log(' Fix the issues above, then try again.\n');
49
+ process.exit(1);
50
+ }
51
+
52
+ console.log('\nāœ… Validation passed! Starting server...\n');
53
+ ```
54
+
55
+ **What this catches:**
56
+ - YAML frontmatter errors (unquoted colons, tabs)
57
+ - MDX compatibility issues (unquoted attributes, unclosed tags)
58
+ - Duplicate routes
59
+ - Broken internal links
60
+
61
+ **Auto-fixes applied:**
62
+ - Wraps YAML values with colons in quotes
63
+ - Quotes HTML attributes
64
+ - Adds closing slashes to void elements (`<br>` → `<br />`)
65
+ - Converts tabs to spaces in YAML
66
+
25
67
  ### Step 2: Check for Cached Installation
26
68
 
27
69
  ```bash