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
@@ -156,8 +156,8 @@ async function executeSearch(query) {
156
156
  "1000"
157
157
  // Max results
158
158
  ], { env: getGhEnv() });
159
- if (result.status !== 0) {
160
- throw new Error(`Failed to search issues: ${result.stderr || result.stdout}`);
159
+ if (result.exitCode !== 0) {
160
+ throw new Error(`Failed to search issues (exit code ${result.exitCode}): ${result.stderr || result.stdout}`);
161
161
  }
162
162
  if (!result.stdout.trim()) {
163
163
  return [];
@@ -317,8 +317,8 @@ async function executeSearch(query: string): Promise<GitHubIssue[]> {
317
317
  '1000', // Max results
318
318
  ], { env: getGhEnv() });
319
319
 
320
- if (result.status !== 0) {
321
- throw new Error(`Failed to search issues: ${result.stderr || result.stdout}`);
320
+ if (result.exitCode !== 0) {
321
+ throw new Error(`Failed to search issues (exit code ${result.exitCode}): ${result.stderr || result.stdout}`);
322
322
  }
323
323
 
324
324
  if (!result.stdout.trim()) {
@@ -251,13 +251,29 @@ ${userStory.technicalContext}
251
251
 
252
252
  \u{1F916} Auto-generated by SpecWeave
253
253
  `;
254
- const existingIssues = await this.octokit.issues.listForRepo({
255
- owner: this.config.owner,
256
- repo,
257
- labels: "specweave",
258
- state: "all"
259
- });
260
- const existing = existingIssues.data.find((issue) => issue.title === title);
254
+ let existing;
255
+ const seenNumbers = /* @__PURE__ */ new Set();
256
+ let page = 1;
257
+ while (!existing) {
258
+ const existingIssues = await this.octokit.issues.listForRepo({
259
+ owner: this.config.owner,
260
+ repo,
261
+ labels: "specweave",
262
+ state: "all",
263
+ per_page: 100,
264
+ page
265
+ });
266
+ for (const issue of existingIssues.data) {
267
+ if (seenNumbers.has(issue.number)) continue;
268
+ seenNumbers.add(issue.number);
269
+ if (issue.title === title) {
270
+ existing = { number: issue.number, title: issue.title };
271
+ break;
272
+ }
273
+ }
274
+ if (existingIssues.data.length < 100) break;
275
+ page++;
276
+ }
261
277
  if (existing) {
262
278
  const response = await this.octokit.issues.update({
263
279
  owner: this.config.owner,
@@ -356,15 +356,33 @@ ${userStory.technicalContext ? `\n## Technical Context\n\n${userStory.technicalC
356
356
  🤖 Auto-generated by SpecWeave
357
357
  `;
358
358
 
359
- // Check if issue already exists (search by title)
360
- const existingIssues = await this.octokit.issues.listForRepo({
361
- owner: this.config.owner,
362
- repo,
363
- labels: 'specweave',
364
- state: 'all'
365
- });
359
+ // Check if issue already exists (search by title, paginated)
360
+ let existing: { number: number; title: string } | undefined;
361
+ const seenNumbers = new Set<number>();
362
+ let page = 1;
363
+
364
+ while (!existing) {
365
+ const existingIssues = await this.octokit.issues.listForRepo({
366
+ owner: this.config.owner,
367
+ repo,
368
+ labels: 'specweave',
369
+ state: 'all',
370
+ per_page: 100,
371
+ page,
372
+ });
366
373
 
367
- const existing = existingIssues.data.find(issue => issue.title === title);
374
+ for (const issue of existingIssues.data) {
375
+ if (seenNumbers.has(issue.number)) continue;
376
+ seenNumbers.add(issue.number);
377
+ if (issue.title === title) {
378
+ existing = { number: issue.number, title: issue.title };
379
+ break;
380
+ }
381
+ }
382
+
383
+ if (existingIssues.data.length < 100) break;
384
+ page++;
385
+ }
368
386
 
369
387
  if (existing) {
370
388
  // Update existing issue
@@ -60,10 +60,38 @@ function parseYamlSimple(yaml) {
60
60
  const stack = [
61
61
  { obj: result, indent: -1 }
62
62
  ];
63
- for (const line of lines) {
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const line = lines[i];
64
65
  if (!line.trim() || line.trim().startsWith("#")) continue;
65
66
  const indent = line.search(/\S/);
66
67
  const trimmed = line.trim();
68
+ if (trimmed.startsWith("- ")) {
69
+ while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
70
+ stack.pop();
71
+ }
72
+ const parent2 = stack[stack.length - 1];
73
+ const key2 = parent2.currentKey;
74
+ if (key2 && parent2.obj[key2] !== void 0) {
75
+ const arr = parent2.obj[key2];
76
+ if (Array.isArray(arr)) {
77
+ const itemText = trimmed.slice(2).trim();
78
+ const nestedColonIdx = itemText.indexOf(":");
79
+ if (nestedColonIdx > 0 && !itemText.startsWith('"') && !itemText.startsWith("'")) {
80
+ const nestedKey = itemText.slice(0, nestedColonIdx).trim();
81
+ const nestedVal = itemText.slice(nestedColonIdx + 1).trim();
82
+ if (nestedVal) {
83
+ const obj = { [nestedKey]: parseYamlValue(nestedVal) };
84
+ arr.push(obj);
85
+ } else {
86
+ arr.push(parseYamlValue(itemText));
87
+ }
88
+ } else {
89
+ arr.push(parseYamlValue(itemText));
90
+ }
91
+ }
92
+ }
93
+ continue;
94
+ }
67
95
  const colonIdx = trimmed.indexOf(":");
68
96
  if (colonIdx === -1) continue;
69
97
  const key = trimmed.slice(0, colonIdx).trim();
@@ -73,15 +101,60 @@ function parseYamlSimple(yaml) {
73
101
  }
74
102
  const parent = stack[stack.length - 1].obj;
75
103
  if (rawValue === "" || rawValue === void 0) {
76
- const child = {};
77
- parent[key] = child;
78
- stack.push({ obj: child, indent });
104
+ const nextIdx = findNextNonEmptyLine(lines, i + 1);
105
+ if (nextIdx !== -1 && lines[nextIdx].trim().startsWith("- ")) {
106
+ parent[key] = [];
107
+ stack.push({ obj: parent, indent, currentKey: key });
108
+ } else {
109
+ const child = {};
110
+ parent[key] = child;
111
+ stack.push({ obj: child, indent });
112
+ }
113
+ } else if (rawValue.startsWith("[") && rawValue.endsWith("]")) {
114
+ parent[key] = parseFlowArray(rawValue);
79
115
  } else {
80
116
  parent[key] = parseYamlValue(rawValue);
81
117
  }
82
118
  }
83
119
  return result;
84
120
  }
121
+ function findNextNonEmptyLine(lines, start) {
122
+ for (let i = start; i < lines.length; i++) {
123
+ const trimmed = lines[i].trim();
124
+ if (trimmed && !trimmed.startsWith("#")) return i;
125
+ }
126
+ return -1;
127
+ }
128
+ function parseFlowArray(raw) {
129
+ const inner = raw.slice(1, -1).trim();
130
+ if (!inner) return [];
131
+ const items = [];
132
+ let current = "";
133
+ let inQuote = false;
134
+ let quoteChar = "";
135
+ for (let i = 0; i < inner.length; i++) {
136
+ const ch = inner[i];
137
+ if (inQuote) {
138
+ if (ch === quoteChar) {
139
+ inQuote = false;
140
+ }
141
+ current += ch;
142
+ } else if (ch === '"' || ch === "'") {
143
+ inQuote = true;
144
+ quoteChar = ch;
145
+ current += ch;
146
+ } else if (ch === ",") {
147
+ items.push(parseYamlValue(current.trim()));
148
+ current = "";
149
+ } else {
150
+ current += ch;
151
+ }
152
+ }
153
+ if (current.trim()) {
154
+ items.push(parseYamlValue(current.trim()));
155
+ }
156
+ return items;
157
+ }
85
158
  function parseYamlValue(raw) {
86
159
  if (raw === "null") return null;
87
160
  if (raw === "true") return true;
@@ -99,7 +172,39 @@ function stringifyYaml(obj, indent = 0) {
99
172
  for (const [key, value] of Object.entries(obj)) {
100
173
  if (value === null || value === void 0) {
101
174
  parts.push(`${prefix}${key}: null`);
102
- } else if (typeof value === "object" && !Array.isArray(value)) {
175
+ } else if (Array.isArray(value)) {
176
+ if (value.length === 0) {
177
+ parts.push(`${prefix}${key}: []`);
178
+ } else if (value.every((v) => typeof v !== "object" || v === null)) {
179
+ parts.push(`${prefix}${key}:`);
180
+ for (const item of value) {
181
+ if (typeof item === "string") {
182
+ parts.push(`${prefix} - "${item}"`);
183
+ } else {
184
+ parts.push(`${prefix} - ${item}`);
185
+ }
186
+ }
187
+ } else {
188
+ parts.push(`${prefix}${key}:`);
189
+ for (const item of value) {
190
+ if (typeof item === "object" && item !== null) {
191
+ const entries = Object.entries(item);
192
+ if (entries.length > 0) {
193
+ const [firstKey, firstVal] = entries[0];
194
+ const formattedVal = typeof firstVal === "string" ? `"${firstVal}"` : firstVal;
195
+ parts.push(`${prefix} - ${firstKey}: ${formattedVal}`);
196
+ for (let j = 1; j < entries.length; j++) {
197
+ const [k, v] = entries[j];
198
+ const fv = typeof v === "string" ? `"${v}"` : v;
199
+ parts.push(`${prefix} ${k}: ${fv}`);
200
+ }
201
+ }
202
+ } else {
203
+ parts.push(`${prefix} - ${item}`);
204
+ }
205
+ }
206
+ }
207
+ } else if (typeof value === "object") {
103
208
  parts.push(`${prefix}${key}:`);
104
209
  parts.push(stringifyYaml(value, indent + 1));
105
210
  } else if (typeof value === "string") {
@@ -101,20 +101,55 @@ export async function updateSpecFrontmatter(
101
101
 
102
102
  /**
103
103
  * Simple YAML parser for spec frontmatter.
104
- * Handles nested objects, strings, numbers, booleans, null.
104
+ * Handles nested objects, strings, numbers, booleans, null, and arrays.
105
+ * Supports block arrays (`- item`) and flow arrays (`[a, b, c]`).
105
106
  */
106
107
  function parseYamlSimple(yaml: string): Record<string, unknown> {
107
108
  const result: Record<string, unknown> = {};
108
109
  const lines = yaml.split('\n');
109
- const stack: Array<{ obj: Record<string, unknown>; indent: number }> = [
110
+ const stack: Array<{ obj: Record<string, unknown>; indent: number; currentKey?: string }> = [
110
111
  { obj: result, indent: -1 },
111
112
  ];
112
113
 
113
- for (const line of lines) {
114
+ for (let i = 0; i < lines.length; i++) {
115
+ const line = lines[i];
114
116
  if (!line.trim() || line.trim().startsWith('#')) continue;
115
117
 
116
118
  const indent = line.search(/\S/);
117
119
  const trimmed = line.trim();
120
+
121
+ // Block array item: `- value` or `- key: value` (nested object in array)
122
+ if (trimmed.startsWith('- ')) {
123
+ // Find the parent that owns this array
124
+ while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
125
+ stack.pop();
126
+ }
127
+ const parent = stack[stack.length - 1];
128
+ const key = parent.currentKey;
129
+ if (key && parent.obj[key] !== undefined) {
130
+ const arr = parent.obj[key];
131
+ if (Array.isArray(arr)) {
132
+ const itemText = trimmed.slice(2).trim();
133
+ // Check if it's a nested object item (- key: value)
134
+ const nestedColonIdx = itemText.indexOf(':');
135
+ if (nestedColonIdx > 0 && !itemText.startsWith('"') && !itemText.startsWith("'")) {
136
+ const nestedKey = itemText.slice(0, nestedColonIdx).trim();
137
+ const nestedVal = itemText.slice(nestedColonIdx + 1).trim();
138
+ if (nestedVal) {
139
+ // Simple key:value object item
140
+ const obj: Record<string, unknown> = { [nestedKey]: parseYamlValue(nestedVal) };
141
+ arr.push(obj);
142
+ } else {
143
+ arr.push(parseYamlValue(itemText));
144
+ }
145
+ } else {
146
+ arr.push(parseYamlValue(itemText));
147
+ }
148
+ }
149
+ }
150
+ continue;
151
+ }
152
+
118
153
  const colonIdx = trimmed.indexOf(':');
119
154
  if (colonIdx === -1) continue;
120
155
 
@@ -129,10 +164,21 @@ function parseYamlSimple(yaml: string): Record<string, unknown> {
129
164
  const parent = stack[stack.length - 1].obj;
130
165
 
131
166
  if (rawValue === '' || rawValue === undefined) {
132
- // Nested object
133
- const child: Record<string, unknown> = {};
134
- parent[key] = child;
135
- stack.push({ obj: child, indent });
167
+ // Check if next non-empty line is a block array item
168
+ const nextIdx = findNextNonEmptyLine(lines, i + 1);
169
+ if (nextIdx !== -1 && lines[nextIdx].trim().startsWith('- ')) {
170
+ // This key holds an array
171
+ parent[key] = [] as unknown[];
172
+ stack.push({ obj: parent, indent, currentKey: key });
173
+ } else {
174
+ // Nested object
175
+ const child: Record<string, unknown> = {};
176
+ parent[key] = child;
177
+ stack.push({ obj: child, indent });
178
+ }
179
+ } else if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
180
+ // Flow array: [a, b, c]
181
+ parent[key] = parseFlowArray(rawValue);
136
182
  } else {
137
183
  parent[key] = parseYamlValue(rawValue);
138
184
  }
@@ -141,6 +187,52 @@ function parseYamlSimple(yaml: string): Record<string, unknown> {
141
187
  return result;
142
188
  }
143
189
 
190
+ /**
191
+ * Find the next non-empty, non-comment line index.
192
+ */
193
+ function findNextNonEmptyLine(lines: string[], start: number): number {
194
+ for (let i = start; i < lines.length; i++) {
195
+ const trimmed = lines[i].trim();
196
+ if (trimmed && !trimmed.startsWith('#')) return i;
197
+ }
198
+ return -1;
199
+ }
200
+
201
+ /**
202
+ * Parse a YAML flow array like [a, b, c] or ["a", "b"]
203
+ */
204
+ function parseFlowArray(raw: string): unknown[] {
205
+ const inner = raw.slice(1, -1).trim();
206
+ if (!inner) return [];
207
+ // Split on commas, respecting quoted strings
208
+ const items: unknown[] = [];
209
+ let current = '';
210
+ let inQuote = false;
211
+ let quoteChar = '';
212
+ for (let i = 0; i < inner.length; i++) {
213
+ const ch = inner[i];
214
+ if (inQuote) {
215
+ if (ch === quoteChar) {
216
+ inQuote = false;
217
+ }
218
+ current += ch;
219
+ } else if (ch === '"' || ch === "'") {
220
+ inQuote = true;
221
+ quoteChar = ch;
222
+ current += ch;
223
+ } else if (ch === ',') {
224
+ items.push(parseYamlValue(current.trim()));
225
+ current = '';
226
+ } else {
227
+ current += ch;
228
+ }
229
+ }
230
+ if (current.trim()) {
231
+ items.push(parseYamlValue(current.trim()));
232
+ }
233
+ return items;
234
+ }
235
+
144
236
  function parseYamlValue(raw: string): unknown {
145
237
  if (raw === 'null') return null;
146
238
  if (raw === 'true') return true;
@@ -155,7 +247,7 @@ function parseYamlValue(raw: string): unknown {
155
247
  }
156
248
 
157
249
  /**
158
- * Simple YAML stringifier.
250
+ * Simple YAML stringifier with array support.
159
251
  */
160
252
  function stringifyYaml(obj: Record<string, unknown>, indent = 0): string {
161
253
  const prefix = ' '.repeat(indent);
@@ -164,7 +256,41 @@ function stringifyYaml(obj: Record<string, unknown>, indent = 0): string {
164
256
  for (const [key, value] of Object.entries(obj)) {
165
257
  if (value === null || value === undefined) {
166
258
  parts.push(`${prefix}${key}: null`);
167
- } else if (typeof value === 'object' && !Array.isArray(value)) {
259
+ } else if (Array.isArray(value)) {
260
+ if (value.length === 0) {
261
+ parts.push(`${prefix}${key}: []`);
262
+ } else if (value.every(v => typeof v !== 'object' || v === null)) {
263
+ // Simple array — use block style
264
+ parts.push(`${prefix}${key}:`);
265
+ for (const item of value) {
266
+ if (typeof item === 'string') {
267
+ parts.push(`${prefix} - "${item}"`);
268
+ } else {
269
+ parts.push(`${prefix} - ${item}`);
270
+ }
271
+ }
272
+ } else {
273
+ // Array of objects — use block style with nested keys
274
+ parts.push(`${prefix}${key}:`);
275
+ for (const item of value) {
276
+ if (typeof item === 'object' && item !== null) {
277
+ const entries = Object.entries(item as Record<string, unknown>);
278
+ if (entries.length > 0) {
279
+ const [firstKey, firstVal] = entries[0];
280
+ const formattedVal = typeof firstVal === 'string' ? `"${firstVal}"` : firstVal;
281
+ parts.push(`${prefix} - ${firstKey}: ${formattedVal}`);
282
+ for (let j = 1; j < entries.length; j++) {
283
+ const [k, v] = entries[j];
284
+ const fv = typeof v === 'string' ? `"${v}"` : v;
285
+ parts.push(`${prefix} ${k}: ${fv}`);
286
+ }
287
+ }
288
+ } else {
289
+ parts.push(`${prefix} - ${item}`);
290
+ }
291
+ }
292
+ }
293
+ } else if (typeof value === 'object') {
168
294
  parts.push(`${prefix}${key}:`);
169
295
  parts.push(stringifyYaml(value as Record<string, unknown>, indent + 1));
170
296
  } else if (typeof value === 'string') {
@@ -3,8 +3,20 @@ import { SpecParser } from "../../../src/core/specs/spec-parser.js";
3
3
  import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
4
4
  import { ProjectContextManager } from "../../../src/core/sync/project-context.js";
5
5
  import { getGitHubAuthFromProject } from "../../../src/utils/auth-helpers.js";
6
+ const DEFAULT_CROSS_TEAM_KEYWORDS = [
7
+ "integration",
8
+ "cross-team",
9
+ "cross-project",
10
+ "shared",
11
+ "common",
12
+ "auth",
13
+ "api-contract",
14
+ "sync"
15
+ ];
6
16
  class GitHubSpecSync {
7
17
  constructor(projectRoot = process.cwd()) {
18
+ /** Configurable keywords for cross-team spec detection. Case-insensitive matching. */
19
+ this.crossTeamKeywords = DEFAULT_CROSS_TEAM_KEYWORDS;
8
20
  this.projectRoot = projectRoot;
9
21
  this.specManager = new SpecMetadataManager(projectRoot);
10
22
  this.projectContextManager = new ProjectContextManager(projectRoot);
@@ -395,15 +407,28 @@ ${acList}
395
407
  }
396
408
  /**
397
409
  * Resolve conflicts
410
+ *
411
+ * GUARD: Only overwrites local spec title if the user explicitly configured
412
+ * "remote-wins" as the conflict resolution strategy. Default conflicts
413
+ * detected by detectConflicts() use 'remote-wins' but this guard ensures
414
+ * API-sourced titles never silently replace local titles.
398
415
  */
399
416
  async resolveConflicts(spec, conflicts) {
400
417
  for (const conflict of conflicts) {
401
418
  if (conflict.resolution === "remote-wins") {
402
- console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins)`);
403
419
  if (conflict.field === "title") {
420
+ const remoteTitle = conflict.remoteValue;
421
+ const remoteSuffix = remoteTitle.replace(/^\[.*?\]\s*/, "");
422
+ const localTitle = spec.metadata.title;
423
+ if (remoteSuffix === localTitle || !remoteSuffix) {
424
+ continue;
425
+ }
426
+ console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins \u2014 explicit remote-wins)`);
404
427
  await this.specManager.saveMetadata(spec.metadata.id, {
405
428
  title: conflict.remoteValue
406
429
  });
430
+ } else {
431
+ console.log(` \u{1F504} Resolving: ${conflict.description} (GitHub wins)`);
407
432
  }
408
433
  }
409
434
  }
@@ -439,18 +464,65 @@ ${acList}
439
464
  return result.data.repositoryOwner.id;
440
465
  }
441
466
  /**
442
- * Fetch GitHub Project details
467
+ * Fetch GitHub Project details via GraphQL ProjectV2 API
443
468
  */
444
469
  async fetchGitHubProject(owner, repo, projectId) {
445
- return {
446
- id: projectId,
447
- title: "Project Title",
448
- number: 1,
449
- url: "https://github.com/...",
450
- state: "open",
451
- owner,
452
- repo
453
- };
470
+ const query = `
471
+ query GetProject($owner: String!, $number: Int!) {
472
+ user(login: $owner) {
473
+ projectV2(number: $number) {
474
+ id
475
+ title
476
+ number
477
+ url
478
+ closed
479
+ }
480
+ }
481
+ }
482
+ `;
483
+ try {
484
+ const result = await this.executeGraphQL(query, {
485
+ owner,
486
+ number: projectId
487
+ });
488
+ let project = result.data?.user?.projectV2;
489
+ if (!project) {
490
+ const orgQuery = `
491
+ query GetOrgProject($owner: String!, $number: Int!) {
492
+ organization(login: $owner) {
493
+ projectV2(number: $number) {
494
+ id
495
+ title
496
+ number
497
+ url
498
+ closed
499
+ }
500
+ }
501
+ }
502
+ `;
503
+ const orgResult = await this.executeGraphQL(orgQuery, {
504
+ owner,
505
+ number: projectId
506
+ });
507
+ project = orgResult.data?.organization?.projectV2;
508
+ }
509
+ if (!project) {
510
+ throw new Error(`GitHub Project #${projectId} not found for ${owner}`);
511
+ }
512
+ return {
513
+ id: projectId,
514
+ title: project.title,
515
+ number: project.number,
516
+ url: project.url,
517
+ state: project.closed ? "closed" : "open",
518
+ owner,
519
+ repo
520
+ };
521
+ } catch (error) {
522
+ throw new Error(
523
+ `Failed to fetch GitHub Project #${projectId}: ${error instanceof Error ? error.message : String(error)}`
524
+ );
525
+ }
454
526
  }
455
527
  /**
456
528
  * Find issue by title pattern
@@ -637,20 +709,9 @@ ${acList}
637
709
  * - Tags include multiple project names
638
710
  */
639
711
  isCrossTeamSpec(spec) {
640
- const crossTeamKeywords = [
641
- "integration",
642
- "cross-team",
643
- "cross-project",
644
- "shared",
645
- "common",
646
- "auth",
647
- // Auth often touches frontend + backend
648
- "api-contract",
649
- "sync"
650
- ];
651
712
  const title = spec.metadata.title.toLowerCase();
652
- const hasCrossTeamKeyword = crossTeamKeywords.some(
653
- (keyword) => title.includes(keyword)
713
+ const hasCrossTeamKeyword = this.crossTeamKeywords.some(
714
+ (keyword) => title.toLowerCase().includes(keyword.toLowerCase())
654
715
  );
655
716
  const tags = spec.metadata.tags || [];
656
717
  const projectTags = tags.filter((tag) => tag.startsWith("project:"));