specweave 1.0.350 → 1.0.352

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 (282) hide show
  1. package/bin/specweave.js +9 -0
  2. package/dist/plugins/specweave-ado/lib/ado-client-v2.d.ts +5 -0
  3. package/dist/plugins/specweave-ado/lib/ado-client-v2.d.ts.map +1 -1
  4. package/dist/plugins/specweave-ado/lib/ado-client-v2.js +61 -23
  5. package/dist/plugins/specweave-ado/lib/ado-client-v2.js.map +1 -1
  6. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.d.ts.map +1 -1
  7. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js +3 -2
  8. package/dist/plugins/specweave-ado/lib/ado-duplicate-detector.js.map +1 -1
  9. package/dist/plugins/specweave-ado/lib/ado-profile-resolver.d.ts.map +1 -1
  10. package/dist/plugins/specweave-ado/lib/ado-profile-resolver.js +2 -1
  11. package/dist/plugins/specweave-ado/lib/ado-profile-resolver.js.map +1 -1
  12. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts +1 -1
  13. package/dist/plugins/specweave-ado/lib/ado-spec-sync.d.ts.map +1 -1
  14. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js +25 -9
  15. package/dist/plugins/specweave-ado/lib/ado-spec-sync.js.map +1 -1
  16. package/dist/plugins/specweave-ado/lib/conflict-resolver.d.ts.map +1 -1
  17. package/dist/plugins/specweave-ado/lib/conflict-resolver.js +17 -1
  18. package/dist/plugins/specweave-ado/lib/conflict-resolver.js.map +1 -1
  19. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +3 -0
  20. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -1
  21. package/dist/plugins/specweave-ado/lib/per-us-sync.js +14 -1
  22. package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -1
  23. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  24. package/dist/plugins/specweave-github/lib/github-client-v2.js +10 -7
  25. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  26. package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
  27. package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
  28. package/dist/plugins/specweave-github/lib/github-client.js +7 -5
  29. package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
  30. package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js +13 -3
  31. package/dist/plugins/specweave-github/lib/github-cross-repo-sync.js.map +1 -1
  32. package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts +24 -1
  33. package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts.map +1 -1
  34. package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js +36 -20
  35. package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js.map +1 -1
  36. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +4 -2
  37. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -1
  38. package/dist/plugins/specweave-github/lib/github-feature-sync.js +38 -9
  39. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -1
  40. package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts +1 -0
  41. package/dist/plugins/specweave-github/lib/github-graphql-client.d.ts.map +1 -1
  42. package/dist/plugins/specweave-github/lib/github-graphql-client.js +32 -22
  43. package/dist/plugins/specweave-github/lib/github-graphql-client.js.map +1 -1
  44. package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +144 -8
  45. package/dist/plugins/specweave-github/lib/github-spec-frontmatter-updater.js.map +1 -1
  46. package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts +8 -1
  47. package/dist/plugins/specweave-github/lib/github-spec-sync.d.ts.map +1 -1
  48. package/dist/plugins/specweave-github/lib/github-spec-sync.js +94 -24
  49. package/dist/plugins/specweave-github/lib/github-spec-sync.js.map +1 -1
  50. package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts +1 -0
  51. package/dist/plugins/specweave-github/lib/github-sync-orchestrator.d.ts.map +1 -1
  52. package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js +2 -1
  53. package/dist/plugins/specweave-github/lib/github-sync-orchestrator.js.map +1 -1
  54. package/dist/plugins/specweave-github/lib/github-us-auto-closer.d.ts.map +1 -1
  55. package/dist/plugins/specweave-github/lib/github-us-auto-closer.js +25 -0
  56. package/dist/plugins/specweave-github/lib/github-us-auto-closer.js.map +1 -1
  57. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +3 -0
  58. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -1
  59. package/dist/plugins/specweave-github/lib/per-us-sync.js +29 -9
  60. package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -1
  61. package/dist/plugins/specweave-jira/lib/content-format-adapter.d.ts +59 -0
  62. package/dist/plugins/specweave-jira/lib/content-format-adapter.d.ts.map +1 -0
  63. package/dist/plugins/specweave-jira/lib/content-format-adapter.js +159 -0
  64. package/dist/plugins/specweave-jira/lib/content-format-adapter.js.map +1 -0
  65. package/dist/plugins/specweave-jira/lib/jira-deployment-detector.d.ts +45 -0
  66. package/dist/plugins/specweave-jira/lib/jira-deployment-detector.d.ts.map +1 -0
  67. package/dist/plugins/specweave-jira/lib/jira-deployment-detector.js +92 -0
  68. package/dist/plugins/specweave-jira/lib/jira-deployment-detector.js.map +1 -0
  69. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.d.ts.map +1 -1
  70. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js +13 -28
  71. package/dist/plugins/specweave-jira/lib/jira-duplicate-detector.js.map +1 -1
  72. package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +2 -1
  73. package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -1
  74. package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +19 -7
  75. package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -1
  76. package/dist/plugins/specweave-jira/lib/jira-field-discovery.d.ts +47 -0
  77. package/dist/plugins/specweave-jira/lib/jira-field-discovery.d.ts.map +1 -0
  78. package/dist/plugins/specweave-jira/lib/jira-field-discovery.js +110 -0
  79. package/dist/plugins/specweave-jira/lib/jira-field-discovery.js.map +1 -0
  80. package/dist/plugins/specweave-jira/lib/jira-paginated-search.d.ts +26 -0
  81. package/dist/plugins/specweave-jira/lib/jira-paginated-search.d.ts.map +1 -0
  82. package/dist/plugins/specweave-jira/lib/jira-paginated-search.js +77 -0
  83. package/dist/plugins/specweave-jira/lib/jira-paginated-search.js.map +1 -0
  84. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.d.ts.map +1 -1
  85. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js +5 -3
  86. package/dist/plugins/specweave-jira/lib/jira-spec-commit-sync.js.map +1 -1
  87. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts +17 -2
  88. package/dist/plugins/specweave-jira/lib/jira-spec-sync.d.ts.map +1 -1
  89. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js +103 -33
  90. package/dist/plugins/specweave-jira/lib/jira-spec-sync.js.map +1 -1
  91. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +4 -0
  92. package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -1
  93. package/dist/plugins/specweave-jira/lib/jira-status-sync.js +19 -6
  94. package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -1
  95. package/dist/plugins/specweave-jira/lib/metadata-paths.d.ts +29 -0
  96. package/dist/plugins/specweave-jira/lib/metadata-paths.d.ts.map +1 -0
  97. package/dist/plugins/specweave-jira/lib/metadata-paths.js +73 -0
  98. package/dist/plugins/specweave-jira/lib/metadata-paths.js.map +1 -0
  99. package/dist/plugins/specweave-jira/lib/reorganization-detector.d.ts +15 -2
  100. package/dist/plugins/specweave-jira/lib/reorganization-detector.d.ts.map +1 -1
  101. package/dist/plugins/specweave-jira/lib/reorganization-detector.js +121 -33
  102. package/dist/plugins/specweave-jira/lib/reorganization-detector.js.map +1 -1
  103. package/dist/src/cli/commands/init.d.ts.map +1 -1
  104. package/dist/src/cli/commands/init.js +23 -18
  105. package/dist/src/cli/commands/init.js.map +1 -1
  106. package/dist/src/cli/commands/sync-progress.d.ts +6 -0
  107. package/dist/src/cli/commands/sync-progress.d.ts.map +1 -1
  108. package/dist/src/cli/commands/sync-progress.js +37 -0
  109. package/dist/src/cli/commands/sync-progress.js.map +1 -1
  110. package/dist/src/cli/commands/sync-task.d.ts +16 -0
  111. package/dist/src/cli/commands/sync-task.d.ts.map +1 -0
  112. package/dist/src/cli/commands/sync-task.js +42 -0
  113. package/dist/src/cli/commands/sync-task.js.map +1 -0
  114. package/dist/src/cli/helpers/init/instruction-file-merger.js +3 -3
  115. package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -1
  116. package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts +9 -1
  117. package/dist/src/core/hooks/LifecycleHookDispatcher.d.ts.map +1 -1
  118. package/dist/src/core/hooks/LifecycleHookDispatcher.js +26 -8
  119. package/dist/src/core/hooks/LifecycleHookDispatcher.js.map +1 -1
  120. package/dist/src/core/increment/metadata-manager.d.ts +13 -0
  121. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  122. package/dist/src/core/increment/metadata-manager.js +144 -17
  123. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  124. package/dist/src/core/increment/status-change-sync-trigger.d.ts +1 -1
  125. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  126. package/dist/src/core/increment/status-change-sync-trigger.js +2 -1
  127. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  128. package/dist/src/core/increment/status-commands.d.ts.map +1 -1
  129. package/dist/src/core/increment/status-commands.js +33 -11
  130. package/dist/src/core/increment/status-commands.js.map +1 -1
  131. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  132. package/dist/src/core/repo-structure/repo-structure-manager.js +2 -1
  133. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  134. package/dist/src/locales/de/cli.json +252 -77
  135. package/dist/src/locales/en/cli.json +7 -0
  136. package/dist/src/locales/es/cli.json +245 -3
  137. package/dist/src/locales/fr/cli.json +259 -84
  138. package/dist/src/locales/ja/cli.json +253 -78
  139. package/dist/src/locales/ko/cli.json +253 -78
  140. package/dist/src/locales/pt/cli.json +252 -77
  141. package/dist/src/locales/ru/cli.json +17 -3
  142. package/dist/src/locales/zh/cli.json +258 -83
  143. package/dist/src/sync/ado-reconciler.d.ts.map +1 -1
  144. package/dist/src/sync/ado-reconciler.js +5 -1
  145. package/dist/src/sync/ado-reconciler.js.map +1 -1
  146. package/dist/src/sync/base-reconciler.d.ts.map +1 -1
  147. package/dist/src/sync/base-reconciler.js +6 -1
  148. package/dist/src/sync/base-reconciler.js.map +1 -1
  149. package/dist/src/sync/config.d.ts +4 -0
  150. package/dist/src/sync/config.d.ts.map +1 -1
  151. package/dist/src/sync/config.js +6 -4
  152. package/dist/src/sync/config.js.map +1 -1
  153. package/dist/src/sync/external-issue-auto-creator.d.ts +3 -0
  154. package/dist/src/sync/external-issue-auto-creator.d.ts.map +1 -1
  155. package/dist/src/sync/external-issue-auto-creator.js +53 -17
  156. package/dist/src/sync/external-issue-auto-creator.js.map +1 -1
  157. package/dist/src/sync/external-item-sync-service.d.ts +9 -0
  158. package/dist/src/sync/external-item-sync-service.d.ts.map +1 -1
  159. package/dist/src/sync/external-item-sync-service.js +210 -9
  160. package/dist/src/sync/external-item-sync-service.js.map +1 -1
  161. package/dist/src/sync/github-reconciler.d.ts +30 -0
  162. package/dist/src/sync/github-reconciler.d.ts.map +1 -1
  163. package/dist/src/sync/github-reconciler.js +242 -3
  164. package/dist/src/sync/github-reconciler.js.map +1 -1
  165. package/dist/src/sync/jira-reconciler.d.ts.map +1 -1
  166. package/dist/src/sync/jira-reconciler.js +5 -1
  167. package/dist/src/sync/jira-reconciler.js.map +1 -1
  168. package/dist/src/sync/provider-router.d.ts.map +1 -1
  169. package/dist/src/sync/provider-router.js +2 -1
  170. package/dist/src/sync/provider-router.js.map +1 -1
  171. package/dist/src/sync/providers/ado.d.ts +4 -0
  172. package/dist/src/sync/providers/ado.d.ts.map +1 -1
  173. package/dist/src/sync/providers/ado.js +36 -11
  174. package/dist/src/sync/providers/ado.js.map +1 -1
  175. package/dist/src/sync/providers/github.d.ts.map +1 -1
  176. package/dist/src/sync/providers/github.js +48 -35
  177. package/dist/src/sync/providers/github.js.map +1 -1
  178. package/dist/src/sync/providers/jira.d.ts.map +1 -1
  179. package/dist/src/sync/providers/jira.js +42 -26
  180. package/dist/src/sync/providers/jira.js.map +1 -1
  181. package/dist/src/sync/status-mapper.d.ts +3 -1
  182. package/dist/src/sync/status-mapper.d.ts.map +1 -1
  183. package/dist/src/sync/status-mapper.js +10 -2
  184. package/dist/src/sync/status-mapper.js.map +1 -1
  185. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  186. package/dist/src/sync/sync-coordinator.js +29 -19
  187. package/dist/src/sync/sync-coordinator.js.map +1 -1
  188. package/package.json +1 -1
  189. package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +31 -0
  190. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +13 -0
  191. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +144 -17
  192. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  193. package/plugins/specweave/lib/vendor/sync/github-reconciler.d.ts +30 -0
  194. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +242 -3
  195. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  196. package/plugins/specweave/skills/architect/SKILL.md +2 -0
  197. package/plugins/specweave/skills/grill/SKILL.md +2 -0
  198. package/plugins/specweave/skills/team-lead/SKILL.md +43 -320
  199. package/plugins/specweave/skills/team-lead/agents/backend.md +60 -0
  200. package/plugins/specweave/skills/team-lead/agents/database.md +51 -0
  201. package/plugins/specweave/skills/team-lead/agents/frontend.md +61 -0
  202. package/plugins/specweave/skills/team-lead/agents/security.md +52 -0
  203. package/plugins/specweave/skills/team-lead/agents/testing.md +57 -0
  204. package/plugins/specweave/skills/test-aware-planner/SKILL.md +2 -0
  205. package/plugins/specweave-ado/hooks/post-task-completion.sh +2 -2
  206. package/plugins/specweave-ado/lib/ado-client-v2.js +51 -21
  207. package/plugins/specweave-ado/lib/ado-client-v2.ts +62 -23
  208. package/plugins/specweave-ado/lib/ado-duplicate-detector.js +4 -4
  209. package/plugins/specweave-ado/lib/ado-duplicate-detector.ts +4 -3
  210. package/plugins/specweave-ado/lib/ado-hierarchical-sync.js +54 -12
  211. package/plugins/specweave-ado/lib/ado-hierarchical-sync.ts +88 -18
  212. package/plugins/specweave-ado/lib/ado-profile-resolver.js +1 -1
  213. package/plugins/specweave-ado/lib/ado-profile-resolver.ts +3 -1
  214. package/plugins/specweave-ado/lib/ado-spec-sync.js +22 -9
  215. package/plugins/specweave-ado/lib/ado-spec-sync.ts +27 -9
  216. package/plugins/specweave-ado/lib/conflict-resolver.js +17 -1
  217. package/plugins/specweave-ado/lib/conflict-resolver.ts +17 -1
  218. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +11 -1
  219. package/plugins/specweave-ado/lib/per-us-sync.js +8 -1
  220. package/plugins/specweave-ado/lib/per-us-sync.ts +17 -2
  221. package/plugins/specweave-github/hooks/github-auto-create-handler.sh +28 -2
  222. package/plugins/specweave-github/hooks/post-task-completion.sh +6 -3
  223. package/plugins/specweave-github/lib/enhanced-github-sync.js +35 -6
  224. package/plugins/specweave-github/lib/github-board-resolver.js +4 -4
  225. package/plugins/specweave-github/lib/github-board-resolver.ts +4 -4
  226. package/plugins/specweave-github/lib/github-client-v2.js +6 -6
  227. package/plugins/specweave-github/lib/github-client-v2.ts +11 -7
  228. package/plugins/specweave-github/lib/github-client.js +5 -4
  229. package/plugins/specweave-github/lib/github-client.ts +7 -5
  230. package/plugins/specweave-github/lib/github-cross-repo-sync.js +17 -3
  231. package/plugins/specweave-github/lib/github-cross-repo-sync.ts +16 -3
  232. package/plugins/specweave-github/lib/github-feature-sync-cli.js +20 -11
  233. package/plugins/specweave-github/lib/github-feature-sync-cli.ts +42 -20
  234. package/plugins/specweave-github/lib/github-feature-sync.js +32 -8
  235. package/plugins/specweave-github/lib/github-feature-sync.ts +41 -9
  236. package/plugins/specweave-github/lib/github-graphql-client.js +29 -20
  237. package/plugins/specweave-github/lib/github-graphql-client.ts +34 -22
  238. package/plugins/specweave-github/lib/github-hierarchical-sync.js +2 -2
  239. package/plugins/specweave-github/lib/github-hierarchical-sync.ts +2 -2
  240. package/plugins/specweave-github/lib/github-multi-project-sync.js +23 -7
  241. package/plugins/specweave-github/lib/github-multi-project-sync.ts +26 -8
  242. package/plugins/specweave-github/lib/github-spec-frontmatter-updater.js +110 -5
  243. package/plugins/specweave-github/lib/github-spec-frontmatter-updater.ts +135 -9
  244. package/plugins/specweave-github/lib/github-spec-sync.js +85 -24
  245. package/plugins/specweave-github/lib/github-spec-sync.ts +100 -26
  246. package/plugins/specweave-github/lib/github-sync-orchestrator.js +2 -1
  247. package/plugins/specweave-github/lib/github-sync-orchestrator.ts +3 -1
  248. package/plugins/specweave-github/lib/github-us-auto-closer.js +25 -0
  249. package/plugins/specweave-github/lib/github-us-auto-closer.ts +43 -0
  250. package/plugins/specweave-github/lib/per-us-sync.js +26 -11
  251. package/plugins/specweave-github/lib/per-us-sync.ts +29 -11
  252. package/plugins/specweave-jira/hooks/post-task-completion.sh +2 -1
  253. package/plugins/specweave-jira/lib/content-format-adapter.js +116 -0
  254. package/plugins/specweave-jira/lib/content-format-adapter.ts +189 -0
  255. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +21 -5
  256. package/plugins/specweave-jira/lib/jira-deployment-detector.js +63 -0
  257. package/plugins/specweave-jira/lib/jira-deployment-detector.ts +113 -0
  258. package/plugins/specweave-jira/lib/jira-duplicate-detector.js +12 -29
  259. package/plugins/specweave-jira/lib/jira-duplicate-detector.ts +13 -27
  260. package/plugins/specweave-jira/lib/jira-epic-sync.js +15 -5
  261. package/plugins/specweave-jira/lib/jira-epic-sync.ts +22 -7
  262. package/plugins/specweave-jira/lib/jira-field-discovery.js +76 -0
  263. package/plugins/specweave-jira/lib/jira-field-discovery.ts +139 -0
  264. package/plugins/specweave-jira/lib/jira-hierarchical-sync.js +10 -0
  265. package/plugins/specweave-jira/lib/jira-hierarchical-sync.ts +11 -0
  266. package/plugins/specweave-jira/lib/jira-multi-project-sync.js +19 -9
  267. package/plugins/specweave-jira/lib/jira-multi-project-sync.ts +25 -14
  268. package/plugins/specweave-jira/lib/jira-paginated-search.js +55 -0
  269. package/plugins/specweave-jira/lib/jira-paginated-search.ts +108 -0
  270. package/plugins/specweave-jira/lib/jira-spec-commit-sync.js +5 -3
  271. package/plugins/specweave-jira/lib/jira-spec-commit-sync.ts +5 -3
  272. package/plugins/specweave-jira/lib/jira-spec-sync.js +102 -31
  273. package/plugins/specweave-jira/lib/jira-spec-sync.ts +123 -45
  274. package/plugins/specweave-jira/lib/jira-status-sync.js +18 -5
  275. package/plugins/specweave-jira/lib/jira-status-sync.ts +21 -6
  276. package/plugins/specweave-jira/lib/metadata-paths.js +38 -0
  277. package/plugins/specweave-jira/lib/metadata-paths.ts +73 -0
  278. package/plugins/specweave-jira/lib/reorganization-detector.js +101 -23
  279. package/plugins/specweave-jira/lib/reorganization-detector.ts +125 -35
  280. package/plugins/specweave-jira/scripts/refresh-cache.js +1 -1
  281. package/plugins/specweave-jira/scripts/refresh-cache.ts +2 -2
  282. package/plugins/specweave-jira/skills/jira-resource-validator/SKILL.md +3 -5
@@ -0,0 +1,57 @@
1
+ You are the TESTING agent for increment [INCREMENT_ID].
2
+
3
+ MASTER SPEC (SOURCE OF TRUTH):
4
+ The feature is fully specified in [MASTER_INCREMENT_PATH]/spec.md.
5
+ This spec defines scope, user stories, and acceptance criteria.
6
+ Your tests MUST cover ALL ACs from the master spec.
7
+ Read the master spec BEFORE planning any work.
8
+
9
+ SKILLS TO INVOKE:
10
+ Skill({ skill: "testing:qa" })
11
+ Skill({ skill: "testing:e2e" }) // for E2E test suites
12
+ Skill({ skill: "testing:unit" }) // for unit test coverage
13
+
14
+ FILE OWNERSHIP (WRITE access):
15
+ tests/**
16
+ __tests__/**
17
+ src/**/*.test.ts
18
+ src/**/*.test.tsx
19
+ src/**/*.spec.ts
20
+ e2e/**
21
+ playwright.config.ts // if Playwright
22
+ cypress.config.ts // if Cypress
23
+ test-utils/**
24
+ fixtures/**
25
+
26
+ READ ACCESS: Any file in the repository
27
+
28
+ WORKFLOW:
29
+ 1. Set working directory to your assigned repo: cd repositories/{ORG}/{repo-name}
30
+ 2. If .specweave/ doesn't exist in your repo, run: specweave init
31
+ 3. Create YOUR increment in YOUR repo: .specweave/increments/[ID]/
32
+ 4. Read the MASTER SPEC at [MASTER_INCREMENT_PATH]/spec.md for scope and ACs
33
+ 5. Wait for ALL other agents to produce initial code
34
+ 6. Create plan files (plan.md, tasks.md) for your increment
35
+ 7. Send plan to team-lead and WAIT for approval:
36
+ SendMessage({ type: "message", recipient: "team-lead",
37
+ content: "PLAN_READY: [increment path]. [summary of test strategy, coverage plan].",
38
+ summary: "Testing plan ready for review" })
39
+ 8. WAIT for "PLAN_APPROVED" message. If "PLAN_REJECTED", revise and re-submit.
40
+ 9. Write unit tests for new services/components
41
+ 10. Write integration tests for API endpoints
42
+ 11. Write E2E tests for user journeys
43
+ 12. Execute tasks autonomously: prefer /sw:auto for autonomous execution
44
+ 13. Run all tests (unit + integration + E2E): npm test && npx playwright test
45
+ 14. Do NOT signal completion until all tests pass -- if tests fail, fix and repeat
46
+ 15. Run quality gate: /sw:grill
47
+ 16. After auto completes, attempt closure via /sw:done
48
+ 17. Signal completion via SendMessage to team-lead
49
+
50
+ RULES:
51
+ - WRITE only to test files (listed above)
52
+ - READ any file for context
53
+ - Tests must cover all acceptance criteria from spec.md
54
+ - Follow existing test patterns and utilities
55
+ - E2E tests must include accessibility checks when applicable
56
+ - ALL repository operations MUST use `repositories/{ORG}/` directory structure
57
+ - Create .specweave/increments/ in YOUR assigned repo, NOT in the umbrella project root
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  description: Generate tasks.md with embedded test plans in BDD format, one user story at a time to prevent crashes. Use for test-first task planning where each task includes Given/When/Then scenarios. Produces implementation tasks with inline test specifications.
3
+ context: fork
4
+ model: opus
3
5
  ---
4
6
 
5
7
  # Test-Aware Planner Skill
@@ -10,7 +10,7 @@
10
10
  # Dependencies:
11
11
  # - Node.js for running sync scripts
12
12
  # - jq for JSON parsing
13
- # - metadata.json must have .ado.item field
13
+ # - metadata.json must have .external_sync.ado.workItemId field (fallback: .ado.item)
14
14
  # - Azure DevOps PAT in .env
15
15
 
16
16
  set +e # EMERGENCY FIX: Prevents Claude Code crashes
@@ -96,7 +96,7 @@ EOF
96
96
  exit 0
97
97
  fi
98
98
 
99
- ADO_ITEM=$(jq -r '.ado.item // empty' "$METADATA_FILE" 2>/dev/null)
99
+ ADO_ITEM=$(jq -r '.external_sync.ado.workItemId // .ado.item // empty' "$METADATA_FILE" 2>/dev/null)
100
100
 
101
101
  if [ -z "$ADO_ITEM" ]; then
102
102
  echo "[$(date)] [ADO] ℹ️ No Azure DevOps work item linked to $CURRENT_INCREMENT, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
@@ -41,6 +41,18 @@ class AdoClientV2 {
41
41
  // ==========================================================================
42
42
  // Authentication & Setup
43
43
  // ==========================================================================
44
+ /**
45
+ * Resolve PAT for an organization.
46
+ * Priority: AZURE_DEVOPS_PAT_{ORG_UPPER} > AZURE_DEVOPS_PAT > fallback
47
+ */
48
+ static resolvePatForOrg(organization, fallbackPat) {
49
+ const orgKey = `AZURE_DEVOPS_PAT_${organization.toUpperCase().replace(/-/g, "_")}`;
50
+ const orgPat = process.env[orgKey];
51
+ if (orgPat) return orgPat;
52
+ const genericPat = process.env.AZURE_DEVOPS_PAT;
53
+ if (genericPat) return genericPat;
54
+ return fallbackPat || "";
55
+ }
44
56
  /**
45
57
  * Test connection and authentication
46
58
  */
@@ -49,11 +61,16 @@ class AdoClientV2 {
49
61
  if (this.isMultiProject) {
50
62
  await this.request("GET", `https://dev.azure.com/${this.organization}/_apis/projects?api-version=7.1`);
51
63
  } else {
52
- await this.request("GET", `/_apis/projects/${this.project}?api-version=7.1`);
64
+ await this.request("GET", `https://dev.azure.com/${this.organization}/_apis/projects/${this.project}?api-version=7.1`);
53
65
  }
54
66
  return { success: true };
55
67
  } catch (error) {
56
- return { success: false, error: error.message };
68
+ const statusMatch = error.message?.match(/HTTP (\d+)/);
69
+ const status = statusMatch ? parseInt(statusMatch[1]) : 0;
70
+ let hint = "";
71
+ if (status === 401) hint = " (check your Personal Access Token)";
72
+ else if (status === 404) hint = " (check organization/project name)";
73
+ return { success: false, error: error.message + hint };
57
74
  }
58
75
  }
59
76
  // ==========================================================================
@@ -207,25 +224,32 @@ class AdoClientV2 {
207
224
  if (queryResult.workItems.length === 0) {
208
225
  return [];
209
226
  }
210
- const ids = queryResult.workItems.map((wi) => wi.id);
227
+ const allIds = queryResult.workItems.map((wi) => wi.id);
211
228
  const batchUrl = this.isMultiProject ? `https://dev.azure.com/${this.organization}/_apis/wit/workitemsbatch?api-version=7.1` : `/_apis/wit/workitemsbatch?api-version=7.1`;
212
- const workItems = await this.request("POST", batchUrl, {
213
- ids,
214
- fields: [
215
- "System.Id",
216
- "System.Title",
217
- "System.Description",
218
- "System.State",
219
- "System.CreatedDate",
220
- "System.ChangedDate",
221
- "System.WorkItemType",
222
- "System.Tags",
223
- "System.AreaPath",
224
- "System.IterationPath",
225
- "System.TeamProject"
226
- ]
227
- });
228
- return workItems.value || [];
229
+ const batchFields = [
230
+ "System.Id",
231
+ "System.Title",
232
+ "System.Description",
233
+ "System.State",
234
+ "System.CreatedDate",
235
+ "System.ChangedDate",
236
+ "System.WorkItemType",
237
+ "System.Tags",
238
+ "System.AreaPath",
239
+ "System.IterationPath",
240
+ "System.TeamProject"
241
+ ];
242
+ const PAGE_SIZE = 200;
243
+ const allWorkItems = [];
244
+ for (let i = 0; i < allIds.length; i += PAGE_SIZE) {
245
+ const pageIds = allIds.slice(i, i + PAGE_SIZE);
246
+ const workItems = await this.request("POST", batchUrl, {
247
+ ids: pageIds,
248
+ fields: batchFields
249
+ });
250
+ allWorkItems.push(...workItems.value || []);
251
+ }
252
+ return allWorkItems;
229
253
  }
230
254
  /**
231
255
  * List work items within time range
@@ -280,7 +304,13 @@ class AdoClientV2 {
280
304
  conditions.push(`[System.CreatedDate] >= '${since}'`);
281
305
  conditions.push(`[System.CreatedDate] <= '${until}'`);
282
306
  if (this.areaPaths && this.areaPaths.length > 0) {
283
- const areaPathConditions = this.areaPaths.map((ap) => `[System.AreaPath] UNDER '${projectName}\\${ap}'`).join(" OR ");
307
+ const areaPathConditions = this.areaPaths.map((ap) => {
308
+ const normalizedAp = ap.replace(/\//g, "\\");
309
+ if (normalizedAp === projectName || normalizedAp.startsWith(`${projectName}\\`)) {
310
+ return `[System.AreaPath] UNDER '${normalizedAp}'`;
311
+ }
312
+ return `[System.AreaPath] UNDER '${projectName}\\${normalizedAp}'`;
313
+ }).join(" OR ");
284
314
  conditions.push(`(${areaPathConditions})`);
285
315
  }
286
316
  if (this.workItemTypes) {
@@ -137,6 +137,24 @@ export class AdoClientV2 {
137
137
  // Authentication & Setup
138
138
  // ==========================================================================
139
139
 
140
+ /**
141
+ * Resolve PAT for an organization.
142
+ * Priority: AZURE_DEVOPS_PAT_{ORG_UPPER} > AZURE_DEVOPS_PAT > fallback
143
+ */
144
+ static resolvePatForOrg(organization: string, fallbackPat?: string): string {
145
+ // Org-specific: AZURE_DEVOPS_PAT_CONTOSO (uppercased, hyphens → underscores)
146
+ const orgKey = `AZURE_DEVOPS_PAT_${organization.toUpperCase().replace(/-/g, '_')}`;
147
+ const orgPat = process.env[orgKey];
148
+ if (orgPat) return orgPat;
149
+
150
+ // Generic PAT
151
+ const genericPat = process.env.AZURE_DEVOPS_PAT;
152
+ if (genericPat) return genericPat;
153
+
154
+ // Fallback (e.g., profile-stored PAT)
155
+ return fallbackPat || '';
156
+ }
157
+
140
158
  /**
141
159
  * Test connection and authentication
142
160
  */
@@ -146,12 +164,17 @@ export class AdoClientV2 {
146
164
  // Test org-level access
147
165
  await this.request('GET', `https://dev.azure.com/${this.organization}/_apis/projects?api-version=7.1`);
148
166
  } else {
149
- // Test project-level access
150
- await this.request('GET', `/_apis/projects/${this.project}?api-version=7.1`);
167
+ // Test project-level access using absolute URL to avoid double project path
168
+ await this.request('GET', `https://dev.azure.com/${this.organization}/_apis/projects/${this.project}?api-version=7.1`);
151
169
  }
152
170
  return { success: true };
153
171
  } catch (error: any) {
154
- return { success: false, error: error.message };
172
+ const statusMatch = error.message?.match(/HTTP (\d+)/);
173
+ const status = statusMatch ? parseInt(statusMatch[1]) : 0;
174
+ let hint = '';
175
+ if (status === 401) hint = ' (check your Personal Access Token)';
176
+ else if (status === 404) hint = ' (check organization/project name)';
177
+ return { success: false, error: error.message + hint };
155
178
  }
156
179
  }
157
180
 
@@ -344,32 +367,41 @@ export class AdoClientV2 {
344
367
  return [];
345
368
  }
346
369
 
347
- // Get full work item details (batch request)
348
- const ids = queryResult.workItems.map((wi) => wi.id);
370
+ // Get full work item details (batch request, paginated in chunks of 200)
371
+ const allIds = queryResult.workItems.map((wi) => wi.id);
349
372
 
350
373
  // For multi-project, use org-level batch API
351
374
  const batchUrl = this.isMultiProject
352
375
  ? `https://dev.azure.com/${this.organization}/_apis/wit/workitemsbatch?api-version=7.1`
353
376
  : `/_apis/wit/workitemsbatch?api-version=7.1`;
354
377
 
355
- const workItems = await this.request('POST', batchUrl, {
356
- ids,
357
- fields: [
358
- 'System.Id',
359
- 'System.Title',
360
- 'System.Description',
361
- 'System.State',
362
- 'System.CreatedDate',
363
- 'System.ChangedDate',
364
- 'System.WorkItemType',
365
- 'System.Tags',
366
- 'System.AreaPath',
367
- 'System.IterationPath',
368
- 'System.TeamProject',
369
- ],
370
- });
378
+ const batchFields = [
379
+ 'System.Id',
380
+ 'System.Title',
381
+ 'System.Description',
382
+ 'System.State',
383
+ 'System.CreatedDate',
384
+ 'System.ChangedDate',
385
+ 'System.WorkItemType',
386
+ 'System.Tags',
387
+ 'System.AreaPath',
388
+ 'System.IterationPath',
389
+ 'System.TeamProject',
390
+ ];
371
391
 
372
- return workItems.value || [];
392
+ const PAGE_SIZE = 200;
393
+ const allWorkItems: WorkItem[] = [];
394
+
395
+ for (let i = 0; i < allIds.length; i += PAGE_SIZE) {
396
+ const pageIds = allIds.slice(i, i + PAGE_SIZE);
397
+ const workItems = await this.request('POST', batchUrl, {
398
+ ids: pageIds,
399
+ fields: batchFields,
400
+ });
401
+ allWorkItems.push(...(workItems.value || []));
402
+ }
403
+
404
+ return allWorkItems;
373
405
  }
374
406
 
375
407
  /**
@@ -457,7 +489,14 @@ export class AdoClientV2 {
457
489
  // Area paths filter (if configured)
458
490
  if (this.areaPaths && this.areaPaths.length > 0) {
459
491
  const areaPathConditions = this.areaPaths
460
- .map((ap: string) => `[System.AreaPath] UNDER '${projectName}\\${ap}'`)
492
+ .map((ap: string) => {
493
+ // Avoid double-prepending project name if path already starts with it
494
+ const normalizedAp = ap.replace(/\//g, '\\');
495
+ if (normalizedAp === projectName || normalizedAp.startsWith(`${projectName}\\`)) {
496
+ return `[System.AreaPath] UNDER '${normalizedAp}'`;
497
+ }
498
+ return `[System.AreaPath] UNDER '${projectName}\\${normalizedAp}'`;
499
+ })
461
500
  .join(' OR ');
462
501
  conditions.push(`(${areaPathConditions})`);
463
502
  }
@@ -53,11 +53,11 @@ class AdoDuplicateDetector {
53
53
  } catch (error) {
54
54
  this.logger.log(`\u26A0\uFE0F Verification check failed: ${error.message}`);
55
55
  return {
56
- success: true,
57
- // Assume success on error
56
+ success: false,
58
57
  expectedCount,
59
- actualCount: expectedCount,
60
- duplicates: []
58
+ actualCount: -1,
59
+ duplicates: [],
60
+ error: `Verification failed: ${error.message}`
61
61
  };
62
62
  }
63
63
  }
@@ -122,11 +122,12 @@ export class AdoDuplicateDetector {
122
122
  } catch (error: any) {
123
123
  this.logger.log(`⚠️ Verification check failed: ${error.message}`);
124
124
  return {
125
- success: true, // Assume success on error
125
+ success: false,
126
126
  expectedCount,
127
- actualCount: expectedCount,
127
+ actualCount: -1,
128
128
  duplicates: [],
129
- };
129
+ error: `Verification failed: ${error.message}`,
130
+ } as VerificationResult & { error: string };
130
131
  }
131
132
  }
132
133
 
@@ -33,16 +33,15 @@ async function buildHierarchicalWIQL(organization, pat, containers) {
33
33
  );
34
34
  }
35
35
  }
36
+ if (container.filters) {
37
+ const containerFilterClauses = buildFilterClauses(container.filters);
38
+ if (containerFilterClauses.length > 0) {
39
+ parts.push(...containerFilterClauses);
40
+ }
41
+ }
36
42
  projectClauses.push(`(${parts.join(" AND ")})`);
37
43
  }
38
44
  let whereClause = projectClauses.join(" OR ");
39
- const filters = containers[0]?.filters;
40
- if (filters) {
41
- const filterClauses = buildFilterClauses(filters);
42
- if (filterClauses.length > 0) {
43
- whereClause = `(${whereClause}) AND ${filterClauses.join(" AND ")}`;
44
- }
45
- }
46
45
  return `
47
46
  SELECT [System.Id], [System.Title], [System.Description], [System.State],
48
47
  [System.CreatedDate], [System.ChangedDate], [System.WorkItemType],
@@ -91,11 +90,18 @@ function addTimeRangeFilter(wiql, timeRange) {
91
90
  }
92
91
  const { since, until } = calculateTimeRange(timeRange);
93
92
  const timeFilter = `[System.CreatedDate] >= '${since}' AND [System.CreatedDate] <= '${until}'`;
94
- if (wiql.includes("ORDER BY")) {
95
- return wiql.replace("ORDER BY", `AND ${timeFilter} ORDER BY`);
96
- } else {
93
+ const orderByRegex = /\bORDER\s+BY\b/i;
94
+ const match = wiql.match(orderByRegex);
95
+ if (match && match.index !== void 0) {
96
+ const before = wiql.substring(0, match.index).trimEnd();
97
+ const after = wiql.substring(match.index);
98
+ return `${before} AND ${timeFilter} ${after}`;
99
+ }
100
+ const whereRegex = /\bWHERE\b/i;
101
+ if (whereRegex.test(wiql)) {
97
102
  return `${wiql} AND ${timeFilter}`;
98
103
  }
104
+ return `${wiql} WHERE ${timeFilter}`;
99
105
  }
100
106
  function calculateTimeRange(timeRange) {
101
107
  const now = /* @__PURE__ */ new Date();
@@ -179,6 +185,10 @@ async function fetchWorkItemsFiltered(config, pat, timeRange) {
179
185
  const baseWiql = await buildHierarchicalWIQL(organization, pat, containers);
180
186
  const wiql = addTimeRangeFilter(baseWiql, timeRange);
181
187
  console.log("\u{1F50D} Fetching work items (FILTERED strategy):", wiql);
188
+ const uniqueProjects = new Set(containers.map((c) => c.id));
189
+ if (uniqueProjects.size > 1) {
190
+ return executeQueryOrgLevel(organization, pat, wiql);
191
+ }
182
192
  const project = containers[0].id;
183
193
  return executeQuery(organization, project, pat, wiql);
184
194
  }
@@ -208,7 +218,34 @@ async function executeQuery(organization, project, pat, wiql) {
208
218
  });
209
219
  return response.value || [];
210
220
  }
211
- function makeRequest(url, pat, method = "GET", body) {
221
+ async function executeQueryOrgLevel(organization, pat, wiql) {
222
+ const baseUrl = `https://dev.azure.com/${organization}`;
223
+ const queryUrl = `${baseUrl}/_apis/wit/wiql?api-version=7.1`;
224
+ const queryResult = await makeRequest(queryUrl, pat, "POST", { query: wiql });
225
+ if (!queryResult.workItems || queryResult.workItems.length === 0) {
226
+ return [];
227
+ }
228
+ const ids = queryResult.workItems.map((wi) => wi.id);
229
+ const batchUrl = `${baseUrl}/_apis/wit/workitemsbatch?api-version=7.1`;
230
+ const response = await makeRequest(batchUrl, pat, "POST", {
231
+ ids,
232
+ fields: [
233
+ "System.Id",
234
+ "System.Title",
235
+ "System.Description",
236
+ "System.State",
237
+ "System.CreatedDate",
238
+ "System.ChangedDate",
239
+ "System.WorkItemType",
240
+ "System.AreaPath",
241
+ "System.IterationPath",
242
+ "System.Tags"
243
+ ]
244
+ });
245
+ return response.value || [];
246
+ }
247
+ const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
248
+ function makeRequest(url, pat, method = "GET", body, timeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
212
249
  return new Promise((resolve, reject) => {
213
250
  const { hostname, pathname, search } = new URL(url);
214
251
  const authHeader = "Basic " + Buffer.from(`:${pat}`).toString("base64");
@@ -223,7 +260,8 @@ function makeRequest(url, pat, method = "GET", body) {
223
260
  hostname,
224
261
  path: pathname + search,
225
262
  method,
226
- headers
263
+ headers,
264
+ timeout: timeoutMs
227
265
  };
228
266
  const req = https.request(options, (res) => {
229
267
  let data = "";
@@ -243,6 +281,10 @@ function makeRequest(url, pat, method = "GET", body) {
243
281
  }
244
282
  });
245
283
  });
284
+ req.on("timeout", () => {
285
+ req.destroy();
286
+ reject(new Error(`ADO request timed out after ${timeoutMs}ms: ${method} ${url}`));
287
+ });
246
288
  req.on("error", (error) => {
247
289
  reject(error);
248
290
  });
@@ -81,6 +81,14 @@ export async function buildHierarchicalWIQL(
81
81
  }
82
82
  }
83
83
 
84
+ // Per-container filters (each container applies its own filters)
85
+ if (container.filters) {
86
+ const containerFilterClauses = buildFilterClauses(container.filters);
87
+ if (containerFilterClauses.length > 0) {
88
+ parts.push(...containerFilterClauses);
89
+ }
90
+ }
91
+
84
92
  // Combine parts with AND
85
93
  projectClauses.push(`(${parts.join(' AND ')})`);
86
94
  }
@@ -88,15 +96,6 @@ export async function buildHierarchicalWIQL(
88
96
  // Build WHERE clause with project clauses
89
97
  let whereClause = projectClauses.join(' OR ');
90
98
 
91
- // Add global filters (apply to all projects)
92
- const filters = containers[0]?.filters;
93
- if (filters) {
94
- const filterClauses = buildFilterClauses(filters);
95
- if (filterClauses.length > 0) {
96
- whereClause = `(${whereClause}) AND ${filterClauses.join(' AND ')}`;
97
- }
98
- }
99
-
100
99
  // Build complete WIQL query
101
100
  return `
102
101
  SELECT [System.Id], [System.Title], [System.Description], [System.State],
@@ -185,15 +184,29 @@ function addTimeRangeFilter(wiql: string, timeRange: string): string {
185
184
 
186
185
  const { since, until } = calculateTimeRange(timeRange as TimeRangePreset);
187
186
 
188
- // Add time range to WHERE clause
189
187
  const timeFilter = `[System.CreatedDate] >= '${since}' AND [System.CreatedDate] <= '${until}'`;
190
188
 
191
- // Insert before ORDER BY if present, otherwise append
192
- if (wiql.includes('ORDER BY')) {
193
- return wiql.replace('ORDER BY', `AND ${timeFilter} ORDER BY`);
194
- } else {
189
+ // Find the last ORDER BY outside of string literals (case-insensitive)
190
+ // Strategy: find ORDER BY keyword, ensuring it's not inside single quotes
191
+ const orderByRegex = /\bORDER\s+BY\b/i;
192
+ const match = wiql.match(orderByRegex);
193
+
194
+ if (match && match.index !== undefined) {
195
+ // Insert AND timeFilter before ORDER BY
196
+ const before = wiql.substring(0, match.index).trimEnd();
197
+ const after = wiql.substring(match.index);
198
+ return `${before} AND ${timeFilter} ${after}`;
199
+ }
200
+
201
+ // No ORDER BY — check if there's a WHERE clause
202
+ const whereRegex = /\bWHERE\b/i;
203
+ if (whereRegex.test(wiql)) {
204
+ // Append to existing WHERE clause
195
205
  return `${wiql} AND ${timeFilter}`;
196
206
  }
207
+
208
+ // No WHERE clause at all — add one
209
+ return `${wiql} WHERE ${timeFilter}`;
197
210
  }
198
211
 
199
212
  /**
@@ -364,9 +377,15 @@ async function fetchWorkItemsFiltered(
364
377
 
365
378
  console.log('🔍 Fetching work items (FILTERED strategy):', wiql);
366
379
 
367
- // Use first project for API endpoint (WIQL can query across projects)
368
- const project = containers[0].id;
380
+ // Detect multi-project: if containers span more than one project, use org-level endpoint
381
+ const uniqueProjects = new Set(containers.map(c => c.id));
382
+ if (uniqueProjects.size > 1) {
383
+ // Cross-project: use org-level WIQL endpoint
384
+ return executeQueryOrgLevel(organization, pat, wiql);
385
+ }
369
386
 
387
+ // Single-project: use project-scoped endpoint
388
+ const project = containers[0].id;
370
389
  return executeQuery(organization, project, pat, wiql);
371
390
  }
372
391
 
@@ -419,13 +438,58 @@ async function executeQuery(
419
438
  }
420
439
 
421
440
  /**
422
- * Make HTTPS request to ADO API
441
+ * Execute WIQL query at org level (cross-project)
442
+ */
443
+ async function executeQueryOrgLevel(
444
+ organization: string,
445
+ pat: string,
446
+ wiql: string
447
+ ): Promise<WorkItem[]> {
448
+ const baseUrl = `https://dev.azure.com/${organization}`;
449
+
450
+ // Execute query at org level
451
+ const queryUrl = `${baseUrl}/_apis/wit/wiql?api-version=7.1`;
452
+ const queryResult: any = await makeRequest(queryUrl, pat, 'POST', { query: wiql });
453
+
454
+ if (!queryResult.workItems || queryResult.workItems.length === 0) {
455
+ return [];
456
+ }
457
+
458
+ // Get full work item details (batch request at org level)
459
+ const ids = queryResult.workItems.map((wi: any) => wi.id);
460
+ const batchUrl = `${baseUrl}/_apis/wit/workitemsbatch?api-version=7.1`;
461
+
462
+ const response: any = await makeRequest(batchUrl, pat, 'POST', {
463
+ ids,
464
+ fields: [
465
+ 'System.Id',
466
+ 'System.Title',
467
+ 'System.Description',
468
+ 'System.State',
469
+ 'System.CreatedDate',
470
+ 'System.ChangedDate',
471
+ 'System.WorkItemType',
472
+ 'System.AreaPath',
473
+ 'System.IterationPath',
474
+ 'System.Tags',
475
+ ],
476
+ });
477
+
478
+ return response.value || [];
479
+ }
480
+
481
+ /** Default request timeout in ms (configurable via ado.requestTimeoutMs) */
482
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
483
+
484
+ /**
485
+ * Make HTTPS request to ADO API with timeout
423
486
  */
424
487
  function makeRequest(
425
488
  url: string,
426
489
  pat: string,
427
490
  method: string = 'GET',
428
- body?: any
491
+ body?: any,
492
+ timeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS
429
493
  ): Promise<any> {
430
494
  return new Promise((resolve, reject) => {
431
495
  const { hostname, pathname, search } = new URL(url);
@@ -446,6 +510,7 @@ function makeRequest(
446
510
  path: pathname + search,
447
511
  method,
448
512
  headers,
513
+ timeout: timeoutMs,
449
514
  };
450
515
 
451
516
  const req = https.request(options, (res) => {
@@ -470,6 +535,11 @@ function makeRequest(
470
535
  });
471
536
  });
472
537
 
538
+ req.on('timeout', () => {
539
+ req.destroy();
540
+ reject(new Error(`ADO request timed out after ${timeoutMs}ms: ${method} ${url}`));
541
+ });
542
+
473
543
  req.on('error', (error) => {
474
544
  reject(error);
475
545
  });
@@ -26,7 +26,7 @@ class AdoProfileResolver {
26
26
  };
27
27
  }
28
28
  const incrementProfile = await this.getIncrementProfile(incrementId);
29
- const globalDefaultProfile = config.sync?.defaultProfile;
29
+ const globalDefaultProfile = config.sync?.activeProfile ?? config.sync?.defaultProfile;
30
30
  const profileName = incrementProfile || globalDefaultProfile;
31
31
  if (!profileName) {
32
32
  return {
@@ -108,6 +108,7 @@ interface ConfigProfile {
108
108
  * Sync config from config.json
109
109
  */
110
110
  interface SyncConfig {
111
+ activeProfile?: string;
111
112
  defaultProfile?: string;
112
113
  profiles?: Record<string, ConfigProfile>;
113
114
  }
@@ -158,7 +159,8 @@ export class AdoProfileResolver {
158
159
  const incrementProfile = await this.getIncrementProfile(incrementId);
159
160
 
160
161
  // Determine which profile to use
161
- const globalDefaultProfile = config.sync?.defaultProfile;
162
+ // Read both activeProfile (canonical) and defaultProfile (legacy) with fallback
163
+ const globalDefaultProfile = config.sync?.activeProfile ?? config.sync?.defaultProfile;
162
164
  const profileName = incrementProfile || globalDefaultProfile;
163
165
 
164
166
  if (!profileName) {