@gitkraken/core-gitlens 0.5.109 → 0.5.111

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 (416) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/dist/git/cache.d.ts +111 -6
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +312 -43
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/context.d.ts +11 -0
  7. package/dist/git/context.d.ts.map +1 -1
  8. package/dist/git/context.js.map +1 -1
  9. package/dist/git/models/fileStatus.d.ts +1 -0
  10. package/dist/git/models/fileStatus.d.ts.map +1 -1
  11. package/dist/git/models/fileStatus.js +1 -0
  12. package/dist/git/models/fileStatus.js.map +1 -1
  13. package/dist/git/models/issue.d.ts +4 -0
  14. package/dist/git/models/issue.d.ts.map +1 -1
  15. package/dist/git/models/mergeConflicts.d.ts +2 -0
  16. package/dist/git/models/mergeConflicts.d.ts.map +1 -1
  17. package/dist/git/models/pullRequest.d.ts +119 -2
  18. package/dist/git/models/pullRequest.d.ts.map +1 -1
  19. package/dist/git/models/pullRequest.js +27 -1
  20. package/dist/git/models/pullRequest.js.map +1 -1
  21. package/dist/git/models/repository.d.ts +32 -5
  22. package/dist/git/models/repository.d.ts.map +1 -1
  23. package/dist/git/models/repository.js +58 -12
  24. package/dist/git/models/repository.js.map +1 -1
  25. package/dist/git/models/statusFile.d.ts.map +1 -1
  26. package/dist/git/models/statusFile.js +3 -0
  27. package/dist/git/models/statusFile.js.map +1 -1
  28. package/dist/git/providers/branches.d.ts +2 -0
  29. package/dist/git/providers/branches.d.ts.map +1 -1
  30. package/dist/git/providers/commits.d.ts +8 -0
  31. package/dist/git/providers/commits.d.ts.map +1 -1
  32. package/dist/git/providers/config.d.ts +19 -1
  33. package/dist/git/providers/config.d.ts.map +1 -1
  34. package/dist/git/providers/operations.d.ts +1 -0
  35. package/dist/git/providers/operations.d.ts.map +1 -1
  36. package/dist/git/providers/pausedOperations.d.ts +2 -0
  37. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  38. package/dist/git/providers/status.d.ts +1 -0
  39. package/dist/git/providers/status.d.ts.map +1 -1
  40. package/dist/git/utils/config.utils.d.ts +3 -0
  41. package/dist/git/utils/config.utils.d.ts.map +1 -0
  42. package/dist/git/utils/config.utils.js +8 -0
  43. package/dist/git/utils/config.utils.js.map +1 -0
  44. package/dist/git/utils/issue.utils.d.ts.map +1 -1
  45. package/dist/git/utils/issue.utils.js +2 -0
  46. package/dist/git/utils/issue.utils.js.map +1 -1
  47. package/dist/git/utils/pausedOperationStatus.utils.d.ts +14 -0
  48. package/dist/git/utils/pausedOperationStatus.utils.d.ts.map +1 -1
  49. package/dist/git/utils/pausedOperationStatus.utils.js +23 -0
  50. package/dist/git/utils/pausedOperationStatus.utils.js.map +1 -1
  51. package/dist/git/utils/pullRequest.utils.d.ts +24 -1
  52. package/dist/git/utils/pullRequest.utils.d.ts.map +1 -1
  53. package/dist/git/utils/pullRequest.utils.js +71 -0
  54. package/dist/git/utils/pullRequest.utils.js.map +1 -1
  55. package/dist/git/watching/changeEvent.d.ts +6 -1
  56. package/dist/git/watching/changeEvent.d.ts.map +1 -1
  57. package/dist/git/watching/classifyChange.d.ts.map +1 -1
  58. package/dist/git/watching/classifyChange.js +14 -1
  59. package/dist/git/watching/classifyChange.js.map +1 -1
  60. package/dist/git/watching/watchService.d.ts.map +1 -1
  61. package/dist/git/watching/watchService.js +18 -3
  62. package/dist/git/watching/watchService.js.map +1 -1
  63. package/dist/git/watching/watchSession.d.ts +8 -1
  64. package/dist/git/watching/watchSession.d.ts.map +1 -1
  65. package/dist/git/watching/watchSession.js +38 -6
  66. package/dist/git/watching/watchSession.js.map +1 -1
  67. package/dist/git/watching/watcherPatterns.d.ts +2 -2
  68. package/dist/git/watching/watcherPatterns.d.ts.map +1 -1
  69. package/dist/git/watching/watcherPatterns.js +1 -1
  70. package/dist/git/watching/watcherPatterns.js.map +1 -1
  71. package/dist/git-cli/exec/git.d.ts +1 -1
  72. package/dist/git-cli/exec/git.d.ts.map +1 -1
  73. package/dist/git-cli/exec/git.js +13 -3
  74. package/dist/git-cli/exec/git.js.map +1 -1
  75. package/dist/git-cli/providers/branches.d.ts +19 -0
  76. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  77. package/dist/git-cli/providers/branches.js +282 -78
  78. package/dist/git-cli/providers/branches.js.map +1 -1
  79. package/dist/git-cli/providers/commits.d.ts +4 -0
  80. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  81. package/dist/git-cli/providers/commits.js +84 -11
  82. package/dist/git-cli/providers/commits.js.map +1 -1
  83. package/dist/git-cli/providers/config.d.ts +42 -0
  84. package/dist/git-cli/providers/config.d.ts.map +1 -1
  85. package/dist/git-cli/providers/config.js +186 -5
  86. package/dist/git-cli/providers/config.js.map +1 -1
  87. package/dist/git-cli/providers/contributors.d.ts.map +1 -1
  88. package/dist/git-cli/providers/contributors.js +1 -1
  89. package/dist/git-cli/providers/contributors.js.map +1 -1
  90. package/dist/git-cli/providers/graph.d.ts +2 -2
  91. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  92. package/dist/git-cli/providers/graph.js +27 -9
  93. package/dist/git-cli/providers/graph.js.map +1 -1
  94. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  95. package/dist/git-cli/providers/operations.js +40 -12
  96. package/dist/git-cli/providers/operations.js.map +1 -1
  97. package/dist/git-cli/providers/pausedOperations.d.ts +5 -0
  98. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  99. package/dist/git-cli/providers/pausedOperations.js +87 -16
  100. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  101. package/dist/git-cli/providers/refs.d.ts +5 -3
  102. package/dist/git-cli/providers/refs.d.ts.map +1 -1
  103. package/dist/git-cli/providers/refs.js +33 -4
  104. package/dist/git-cli/providers/refs.js.map +1 -1
  105. package/dist/git-cli/providers/status.d.ts +1 -0
  106. package/dist/git-cli/providers/status.d.ts.map +1 -1
  107. package/dist/git-cli/providers/status.js +18 -3
  108. package/dist/git-cli/providers/status.js.map +1 -1
  109. package/dist/git-cli/providers/worktrees.d.ts.map +1 -1
  110. package/dist/git-cli/providers/worktrees.js +4 -0
  111. package/dist/git-cli/providers/worktrees.js.map +1 -1
  112. package/dist/git-cli/service.d.ts.map +1 -1
  113. package/dist/git-cli/service.js.map +1 -1
  114. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +39 -1
  115. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -1
  116. package/dist/plus/agents/providers/claudeCodeProvider.js +978 -71
  117. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -1
  118. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +24 -2
  119. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -1
  120. package/dist/plus/agents/providers/claudeCodeTranscript.js +84 -9
  121. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  122. package/dist/plus/agents/types.d.ts +37 -2
  123. package/dist/plus/agents/types.d.ts.map +1 -1
  124. package/dist/plus/agents/types.js +2 -0
  125. package/dist/plus/agents/types.js.map +1 -1
  126. package/dist/plus/ai/errors.d.ts +10 -0
  127. package/dist/plus/ai/errors.d.ts.map +1 -1
  128. package/dist/plus/ai/errors.js +29 -0
  129. package/dist/plus/ai/errors.js.map +1 -1
  130. package/dist/plus/ai/models/model.d.ts +4 -0
  131. package/dist/plus/ai/models/model.d.ts.map +1 -1
  132. package/dist/plus/ai/models/model.js +51 -6
  133. package/dist/plus/ai/models/model.js.map +1 -1
  134. package/dist/plus/ai/models/promptTemplates.d.ts +1 -1
  135. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  136. package/dist/plus/ai/models/provider.d.ts +60 -2
  137. package/dist/plus/ai/models/provider.d.ts.map +1 -1
  138. package/dist/plus/ai/prompts.d.ts +12 -0
  139. package/dist/plus/ai/prompts.d.ts.map +1 -1
  140. package/dist/plus/ai/prompts.js +177 -61
  141. package/dist/plus/ai/prompts.js.map +1 -1
  142. package/dist/plus/ai/providers/anthropicProvider.d.ts +10 -2
  143. package/dist/plus/ai/providers/anthropicProvider.d.ts.map +1 -1
  144. package/dist/plus/ai/providers/anthropicProvider.js +139 -46
  145. package/dist/plus/ai/providers/anthropicProvider.js.map +1 -1
  146. package/dist/plus/ai/providers/deepSeekProvider.d.ts +2 -0
  147. package/dist/plus/ai/providers/deepSeekProvider.d.ts.map +1 -1
  148. package/dist/plus/ai/providers/deepSeekProvider.js +10 -21
  149. package/dist/plus/ai/providers/deepSeekProvider.js.map +1 -1
  150. package/dist/plus/ai/providers/geminiProvider.d.ts +1 -0
  151. package/dist/plus/ai/providers/geminiProvider.d.ts.map +1 -1
  152. package/dist/plus/ai/providers/geminiProvider.js +19 -0
  153. package/dist/plus/ai/providers/geminiProvider.js.map +1 -1
  154. package/dist/plus/ai/providers/gitkrakenProvider.d.ts +5 -1
  155. package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +1 -1
  156. package/dist/plus/ai/providers/gitkrakenProvider.js +118 -3
  157. package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
  158. package/dist/plus/ai/providers/huggingFaceProvider.d.ts +1 -0
  159. package/dist/plus/ai/providers/huggingFaceProvider.d.ts.map +1 -1
  160. package/dist/plus/ai/providers/huggingFaceProvider.js +5 -0
  161. package/dist/plus/ai/providers/huggingFaceProvider.js.map +1 -1
  162. package/dist/plus/ai/providers/mistralProvider.d.ts +3 -1
  163. package/dist/plus/ai/providers/mistralProvider.d.ts.map +1 -1
  164. package/dist/plus/ai/providers/mistralProvider.js +99 -19
  165. package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
  166. package/dist/plus/ai/providers/ollamaProvider.d.ts +4 -3
  167. package/dist/plus/ai/providers/ollamaProvider.d.ts.map +1 -1
  168. package/dist/plus/ai/providers/ollamaProvider.js +38 -3
  169. package/dist/plus/ai/providers/ollamaProvider.js.map +1 -1
  170. package/dist/plus/ai/providers/openAICompatibleProvider.d.ts +1 -0
  171. package/dist/plus/ai/providers/openAICompatibleProvider.d.ts.map +1 -1
  172. package/dist/plus/ai/providers/openAICompatibleProvider.js +4 -0
  173. package/dist/plus/ai/providers/openAICompatibleProvider.js.map +1 -1
  174. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +154 -5
  175. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +1 -1
  176. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +259 -10
  177. package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
  178. package/dist/plus/ai/providers/openRouterProvider.d.ts +4 -0
  179. package/dist/plus/ai/providers/openRouterProvider.d.ts.map +1 -1
  180. package/dist/plus/ai/providers/openRouterProvider.js +9 -0
  181. package/dist/plus/ai/providers/openRouterProvider.js.map +1 -1
  182. package/dist/plus/ai/providers/openaiProvider.d.ts +1 -0
  183. package/dist/plus/ai/providers/openaiProvider.d.ts.map +1 -1
  184. package/dist/plus/ai/providers/openaiProvider.js +1 -0
  185. package/dist/plus/ai/providers/openaiProvider.js.map +1 -1
  186. package/dist/plus/ai/providers/responseFormatCache.d.ts +8 -0
  187. package/dist/plus/ai/providers/responseFormatCache.d.ts.map +1 -0
  188. package/dist/plus/ai/providers/responseFormatCache.js +25 -0
  189. package/dist/plus/ai/providers/responseFormatCache.js.map +1 -0
  190. package/dist/plus/ai/providers/xaiProvider.d.ts +1 -0
  191. package/dist/plus/ai/providers/xaiProvider.d.ts.map +1 -1
  192. package/dist/plus/ai/providers/xaiProvider.js +10 -1
  193. package/dist/plus/ai/providers/xaiProvider.js.map +1 -1
  194. package/dist/plus/ai/utils/ai.utils.d.ts +3 -0
  195. package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
  196. package/dist/plus/ai/utils/ai.utils.js +13 -0
  197. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  198. package/dist/plus/ai/utils/results.utils.d.ts +16 -2
  199. package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
  200. package/dist/plus/ai/utils/results.utils.js +204 -28
  201. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  202. package/dist/plus/git-github/api/github.d.ts +92 -4
  203. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  204. package/dist/plus/git-github/api/github.js +324 -20
  205. package/dist/plus/git-github/api/github.js.map +1 -1
  206. package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts +27 -1
  207. package/dist/plus/git-github/api/pullRequestSearchQuery.d.ts.map +1 -1
  208. package/dist/plus/git-github/api/pullRequestSearchQuery.js +73 -6
  209. package/dist/plus/git-github/api/pullRequestSearchQuery.js.map +1 -1
  210. package/dist/plus/git-github/models.d.ts +33 -5
  211. package/dist/plus/git-github/models.d.ts.map +1 -1
  212. package/dist/plus/git-github/models.js +43 -4
  213. package/dist/plus/git-github/models.js.map +1 -1
  214. package/dist/plus/git-github/providers/github/status.d.ts +1 -0
  215. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  216. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  217. package/dist/plus/integrations/context.d.ts +4 -0
  218. package/dist/plus/integrations/context.d.ts.map +1 -1
  219. package/dist/plus/integrations/index.d.ts.map +1 -1
  220. package/dist/plus/integrations/index.js +2 -0
  221. package/dist/plus/integrations/index.js.map +1 -1
  222. package/dist/plus/integrations/integrationService.d.ts +16 -1
  223. package/dist/plus/integrations/integrationService.d.ts.map +1 -1
  224. package/dist/plus/integrations/integrationService.js +9 -1
  225. package/dist/plus/integrations/integrationService.js.map +1 -1
  226. package/dist/plus/integrations/manager.d.ts +45 -5
  227. package/dist/plus/integrations/manager.d.ts.map +1 -1
  228. package/dist/plus/integrations/models/gitHostIntegration.d.ts +18 -3
  229. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
  230. package/dist/plus/integrations/models/gitHostIntegration.js +30 -2
  231. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  232. package/dist/plus/integrations/models/integration.d.ts +4 -2
  233. package/dist/plus/integrations/models/integration.d.ts.map +1 -1
  234. package/dist/plus/integrations/models/integration.js +2 -2
  235. package/dist/plus/integrations/models/integration.js.map +1 -1
  236. package/dist/plus/integrations/providerFilters.d.ts +1 -1
  237. package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
  238. package/dist/plus/integrations/providers/accounts.d.ts +6 -0
  239. package/dist/plus/integrations/providers/accounts.d.ts.map +1 -0
  240. package/dist/plus/integrations/providers/accounts.js +34 -0
  241. package/dist/plus/integrations/providers/accounts.js.map +1 -0
  242. package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -1
  243. package/dist/plus/integrations/providers/azure/models.js +3 -0
  244. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  245. package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -1
  246. package/dist/plus/integrations/providers/bitbucket/models.js +1 -0
  247. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  248. package/dist/plus/integrations/providers/github.d.ts +23 -2
  249. package/dist/plus/integrations/providers/github.d.ts.map +1 -1
  250. package/dist/plus/integrations/providers/github.js +169 -138
  251. package/dist/plus/integrations/providers/github.js.map +1 -1
  252. package/dist/plus/integrations/providers/gitlab.d.ts +9 -0
  253. package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
  254. package/dist/plus/integrations/providers/gitlab.js +23 -4
  255. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  256. package/dist/plus/integrations/providers/models.d.ts +14 -32
  257. package/dist/plus/integrations/providers/models.d.ts.map +1 -1
  258. package/dist/plus/integrations/providers/models.js +66 -109
  259. package/dist/plus/integrations/providers/models.js.map +1 -1
  260. package/dist/plus/integrations/providers/pullRequestReviews.d.ts +50 -0
  261. package/dist/plus/integrations/providers/pullRequestReviews.d.ts.map +1 -0
  262. package/dist/plus/integrations/providers/pullRequestReviews.js +99 -0
  263. package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -0
  264. package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
  265. package/dist/plus/integrations/reads/broaden.js +6 -1
  266. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  267. package/dist/plus/integrations/reads/counts.d.ts +55 -0
  268. package/dist/plus/integrations/reads/counts.d.ts.map +1 -1
  269. package/dist/plus/integrations/reads/counts.js +146 -2
  270. package/dist/plus/integrations/reads/counts.js.map +1 -1
  271. package/dist/plus/integrations/reads/drains.d.ts +7 -1
  272. package/dist/plus/integrations/reads/drains.d.ts.map +1 -1
  273. package/dist/plus/integrations/reads/drains.js +20 -6
  274. package/dist/plus/integrations/reads/drains.js.map +1 -1
  275. package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
  276. package/dist/plus/integrations/reads/filters.js +22 -0
  277. package/dist/plus/integrations/reads/filters.js.map +1 -1
  278. package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
  279. package/dist/plus/integrations/reads/hierarchy.js +3 -1
  280. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  281. package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
  282. package/dist/plus/integrations/reads/issueTracker.js +8 -3
  283. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  284. package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
  285. package/dist/plus/integrations/reads/issues.js +9 -4
  286. package/dist/plus/integrations/reads/issues.js.map +1 -1
  287. package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
  288. package/dist/plus/integrations/reads/pullRequests.js +9 -4
  289. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  290. package/dist/plus/integrations/reads/searchPullRequests.d.ts +4 -3
  291. package/dist/plus/integrations/reads/searchPullRequests.d.ts.map +1 -1
  292. package/dist/plus/integrations/reads/searchPullRequests.js +6 -4
  293. package/dist/plus/integrations/reads/searchPullRequests.js.map +1 -1
  294. package/dist/plus/integrations/reads/sweeps.js +1 -1
  295. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  296. package/dist/plus/integrations/reads/warnings.d.ts +2 -2
  297. package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
  298. package/dist/plus/integrations/reads/warnings.js +11 -2
  299. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  300. package/dist/plus/integrations/results.d.ts +6 -1
  301. package/dist/plus/integrations/results.d.ts.map +1 -1
  302. package/dist/plus/integrations/results.js.map +1 -1
  303. package/dist/utils/env/browser/base64.d.ts +1 -1
  304. package/dist/utils/env/browser/base64.d.ts.map +1 -1
  305. package/dist/utils/env/node/base64.d.ts +1 -1
  306. package/dist/utils/env/node/base64.d.ts.map +1 -1
  307. package/dist/utils/env/node/base64.js +2 -0
  308. package/dist/utils/env/node/base64.js.map +1 -1
  309. package/dist/utils/keys/chord.d.ts +52 -0
  310. package/dist/utils/keys/chord.d.ts.map +1 -0
  311. package/dist/utils/keys/chord.js +135 -0
  312. package/dist/utils/keys/chord.js.map +1 -0
  313. package/dist/utils/keys/keybinding.d.ts +53 -0
  314. package/dist/utils/keys/keybinding.d.ts.map +1 -0
  315. package/dist/utils/keys/keybinding.js +58 -0
  316. package/dist/utils/keys/keybinding.js.map +1 -0
  317. package/dist/utils/path.d.ts +11 -0
  318. package/dist/utils/path.d.ts.map +1 -1
  319. package/dist/utils/path.js +13 -0
  320. package/dist/utils/path.js.map +1 -1
  321. package/docs/integrations.md +76 -10
  322. package/docs/kepler-read-api-parity.md +9 -0
  323. package/package.json +13 -5
  324. package/src/git/cache.ts +383 -47
  325. package/src/git/context.ts +11 -0
  326. package/src/git/models/fileStatus.ts +1 -0
  327. package/src/git/models/issue.ts +4 -0
  328. package/src/git/models/mergeConflicts.ts +3 -2
  329. package/src/git/models/pullRequest.ts +117 -1
  330. package/src/git/models/repository.ts +58 -17
  331. package/src/git/models/statusFile.ts +3 -0
  332. package/src/git/providers/branches.ts +6 -0
  333. package/src/git/providers/commits.ts +11 -0
  334. package/src/git/providers/config.ts +18 -0
  335. package/src/git/providers/operations.ts +1 -0
  336. package/src/git/providers/pausedOperations.ts +2 -0
  337. package/src/git/providers/status.ts +1 -0
  338. package/src/git/utils/config.utils.ts +8 -0
  339. package/src/git/utils/issue.utils.ts +2 -0
  340. package/src/git/utils/pausedOperationStatus.utils.ts +28 -0
  341. package/src/git/utils/pullRequest.utils.ts +86 -0
  342. package/src/git/watching/changeEvent.ts +6 -1
  343. package/src/git/watching/classifyChange.ts +15 -1
  344. package/src/git/watching/watchService.ts +20 -3
  345. package/src/git/watching/watchSession.ts +41 -6
  346. package/src/git/watching/watcherPatterns.ts +1 -1
  347. package/src/git-cli/exec/git.ts +13 -3
  348. package/src/git-cli/providers/branches.ts +384 -132
  349. package/src/git-cli/providers/commits.ts +106 -15
  350. package/src/git-cli/providers/config.ts +229 -8
  351. package/src/git-cli/providers/contributors.ts +5 -1
  352. package/src/git-cli/providers/graph.ts +26 -13
  353. package/src/git-cli/providers/operations.ts +45 -20
  354. package/src/git-cli/providers/pausedOperations.ts +109 -17
  355. package/src/git-cli/providers/refs.ts +33 -3
  356. package/src/git-cli/providers/status.ts +25 -4
  357. package/src/git-cli/providers/worktrees.ts +5 -0
  358. package/src/git-cli/service.ts +0 -1
  359. package/src/plus/agents/providers/claudeCodeProvider.ts +1130 -87
  360. package/src/plus/agents/providers/claudeCodeTranscript.ts +95 -9
  361. package/src/plus/agents/types.ts +46 -2
  362. package/src/plus/ai/errors.ts +30 -0
  363. package/src/plus/ai/models/model.ts +56 -6
  364. package/src/plus/ai/models/promptTemplates.ts +1 -1
  365. package/src/plus/ai/models/provider.ts +69 -2
  366. package/src/plus/ai/prompts.ts +186 -61
  367. package/src/plus/ai/providers/anthropicProvider.ts +155 -47
  368. package/src/plus/ai/providers/deepSeekProvider.ts +11 -21
  369. package/src/plus/ai/providers/geminiProvider.ts +19 -0
  370. package/src/plus/ai/providers/gitkrakenProvider.ts +126 -3
  371. package/src/plus/ai/providers/huggingFaceProvider.ts +6 -0
  372. package/src/plus/ai/providers/mistralProvider.ts +102 -19
  373. package/src/plus/ai/providers/ollamaProvider.ts +55 -4
  374. package/src/plus/ai/providers/openAICompatibleProvider.ts +5 -0
  375. package/src/plus/ai/providers/openAICompatibleProviderBase.ts +360 -17
  376. package/src/plus/ai/providers/openRouterProvider.ts +17 -0
  377. package/src/plus/ai/providers/openaiProvider.ts +1 -0
  378. package/src/plus/ai/providers/responseFormatCache.ts +39 -0
  379. package/src/plus/ai/providers/xaiProvider.ts +10 -1
  380. package/src/plus/ai/utils/ai.utils.ts +14 -0
  381. package/src/plus/ai/utils/results.utils.ts +255 -31
  382. package/src/plus/git-github/api/github.ts +467 -23
  383. package/src/plus/git-github/api/pullRequestSearchQuery.ts +81 -7
  384. package/src/plus/git-github/models.ts +77 -7
  385. package/src/plus/git-github/providers/github/status.ts +1 -0
  386. package/src/plus/integrations/context.ts +4 -0
  387. package/src/plus/integrations/index.ts +2 -0
  388. package/src/plus/integrations/integrationService.ts +25 -2
  389. package/src/plus/integrations/manager.ts +50 -5
  390. package/src/plus/integrations/models/gitHostIntegration.ts +54 -2
  391. package/src/plus/integrations/models/integration.ts +24 -13
  392. package/src/plus/integrations/providerFilters.ts +6 -1
  393. package/src/plus/integrations/providers/accounts.ts +38 -0
  394. package/src/plus/integrations/providers/azure/models.ts +3 -0
  395. package/src/plus/integrations/providers/bitbucket/models.ts +1 -0
  396. package/src/plus/integrations/providers/github.ts +218 -145
  397. package/src/plus/integrations/providers/gitlab.ts +30 -20
  398. package/src/plus/integrations/providers/models.ts +99 -127
  399. package/src/plus/integrations/providers/pullRequestReviews.ts +122 -0
  400. package/src/plus/integrations/reads/broaden.ts +6 -1
  401. package/src/plus/integrations/reads/counts.ts +273 -2
  402. package/src/plus/integrations/reads/drains.ts +19 -6
  403. package/src/plus/integrations/reads/filters.ts +22 -0
  404. package/src/plus/integrations/reads/hierarchy.ts +3 -1
  405. package/src/plus/integrations/reads/issueTracker.ts +9 -3
  406. package/src/plus/integrations/reads/issues.ts +9 -4
  407. package/src/plus/integrations/reads/pullRequests.ts +9 -4
  408. package/src/plus/integrations/reads/searchPullRequests.ts +12 -4
  409. package/src/plus/integrations/reads/sweeps.ts +1 -1
  410. package/src/plus/integrations/reads/warnings.ts +12 -1
  411. package/src/plus/integrations/results.ts +6 -1
  412. package/src/utils/env/browser/base64.ts +1 -1
  413. package/src/utils/env/node/base64.ts +3 -1
  414. package/src/utils/keys/chord.ts +184 -0
  415. package/src/utils/keys/keybinding.ts +116 -0
  416. package/src/utils/path.ts +14 -0
@@ -0,0 +1,38 @@
1
+ import type { Account as ProviderAccount } from '@gitkraken/provider-apis';
2
+ import type { IssueMember } from '../../../git/models/issue.js';
3
+ import type { PullRequestMember } from '../../../git/models/pullRequest.js';
4
+
5
+ export function toProviderAccount(account: PullRequestMember | IssueMember): ProviderAccount {
6
+ return {
7
+ // Stays the provider id because the categorizer matches the viewer to a pull request's people by `id`,
8
+ // and every provider but GitHub already agrees on that namespace. Re-keying to the login breaks Azure,
9
+ // whose account `username` is a display name while its members' is a UPN — different namespaces.
10
+ id: account.id ?? null,
11
+ avatarUrl: account.avatarUrl ?? null,
12
+ name: account.name ?? null,
13
+ url: account.url ?? null,
14
+ // TODO: Implement these in our own model
15
+ email: '',
16
+ username: account.name ?? null,
17
+ };
18
+ }
19
+
20
+ export function fromProviderAccount(account: ProviderAccount | null): PullRequestMember | IssueMember {
21
+ return {
22
+ id: account?.id ?? '',
23
+ // An absent name stays absent. `'unknown'` was a display fallback invented in the provider layer, and a
24
+ // consumer couldn't tell it apart from a member genuinely named that — so it couldn't be undone where a
25
+ // name-shaped placeholder is wrong (rendering an avatar-only chip, or building an AI prompt, where
26
+ // `Assignees: unknown` reads as a real assignee). It also disagreed with {@link toIssueShape}, which
27
+ // collapsed the same absent name to `''` — same facade method, two fallbacks. Both now emit `undefined`.
28
+ name: account?.name ?? undefined,
29
+ username: account?.username ?? undefined,
30
+ avatarUrl: account?.avatarUrl ?? undefined,
31
+ // `url` is optional, so an absent one must be `undefined`, not `''`. `''` passes a `!= null` presence
32
+ // check and renders as a link to nowhere, and it disagreed with {@link toIssueShape} — which already
33
+ // collapses to `undefined` — even though BOTH mappers feed `listIssuesPage` (the repo-scoped path goes
34
+ // through `toIssueShape`, Azure's account-wide path through {@link fromProviderIssue}). Same facade
35
+ // method, two shapes. Matches {@link toProviderRepositoryShape}, which collapses every absent optional.
36
+ url: account?.url ?? undefined,
37
+ };
38
+ }
@@ -475,6 +475,7 @@ export function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): Pu
475
475
  avatarUrl: reviewer.imageUrl,
476
476
  id: reviewer.id,
477
477
  name: reviewer.displayName,
478
+ username: reviewer.uniqueName,
478
479
  url: reviewer.url,
479
480
  },
480
481
  state: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),
@@ -505,6 +506,7 @@ export function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullR
505
506
  avatarUrl: reviewer.imageUrl,
506
507
  id: reviewer.id,
507
508
  name: reviewer.displayName,
509
+ username: reviewer.uniqueName,
508
510
  url: reviewer.url,
509
511
  };
510
512
  }
@@ -520,6 +522,7 @@ function fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'):
520
522
  avatarUrl: user.imageUrl,
521
523
  id: user.id,
522
524
  name: user.displayName,
525
+ username: user.uniqueName,
523
526
  url: user.url,
524
527
  };
525
528
  }
@@ -297,6 +297,7 @@ export function fromBitbucketUser(user: BitbucketUser): PullRequestMember {
297
297
  return {
298
298
  avatarUrl: user.links.avatar.href,
299
299
  name: user.display_name,
300
+ username: user.nickname,
300
301
  url: user.links.html.href,
301
302
  id: user.uuid,
302
303
  };
@@ -1,3 +1,4 @@
1
+ import { gitHubPullRequestRelationshipQualifiers } from '../../git-github/api/pullRequestSearchQuery.js';
1
2
  import type { Account, UnidentifiedAuthor } from '../../../git/models/author.js';
2
3
  import type { DefaultBranch } from '../../../git/models/defaultBranch.js';
3
4
  import type { Issue, IssueSearchCriteria, IssueShape } from '../../../git/models/issue.js';
@@ -6,6 +7,7 @@ import type {
6
7
  PullRequest,
7
8
  PullRequestMergeMethod,
8
9
  PullRequestSearchCriteria,
10
+ PullRequestStackInfo,
9
11
  PullRequestState,
10
12
  PullRequestStateFilter,
11
13
  } from '../../../git/models/pullRequest.js';
@@ -50,17 +52,6 @@ import {
50
52
  } from './models.js';
51
53
  import type { ProvidersApi } from './providersApi.js';
52
54
 
53
- /**
54
- * Page size for GitHub's account-wide search reads: the maximum GitHub's search connection accepts, and what
55
- * `GitHubApi.searchMyPullRequestsPage` already caps itself to.
56
- *
57
- * Requested explicitly rather than left to the SDK's own fallback. That fallback is also 100 as of
58
- * `@gitkraken/provider-apis` 0.54.0 (it was 15 before, which cost a drained sweep ~7x the round trips it needs),
59
- * so this is now a pin rather than an override — it keeps the size this read is tuned for from silently tracking
60
- * a shared SDK default that other reads may want smaller.
61
- */
62
- const githubSearchMaxPageSize = 100;
63
-
64
55
  type GitHubPullRequestFacetCursor = Record<string, string>;
65
56
 
66
57
  function toPullRequestFacetCursor(value: unknown): GitHubPullRequestFacetCursor {
@@ -91,13 +82,6 @@ function parsePullRequestFacetCursor(cursor: string | undefined): GitHubPullRequ
91
82
  }
92
83
  }
93
84
 
94
- const pullRequestRelationshipQualifier: Record<PullRequestFilter, string> = {
95
- [PullRequestFilter.Author]: 'author:@me',
96
- [PullRequestFilter.Assignee]: 'assignee:@me',
97
- [PullRequestFilter.ReviewRequested]: 'review-requested:@me',
98
- [PullRequestFilter.Mention]: 'mentions:@me',
99
- };
100
-
101
85
  const metadata = providersMetadata[GitCloudHostIntegrationId.GitHub];
102
86
  const authProvider: IntegrationAuthenticationProviderDescriptor = Object.freeze({
103
87
  id: metadata.id,
@@ -441,140 +425,124 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
441
425
  },
442
426
  ): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined> {
443
427
  const explicitFilters = options?.filters?.length ? [...new Set(options.filters)] : undefined;
444
- // Explicit relationships need independent search facets so the returned union is exact. State-only reads
445
- // retain the provider-native `involves:@me` behavior for backward compatibility.
446
- if ((options?.state?.length ?? 0) > 0 || explicitFilters != null || options?.includeReviewRequested === true) {
447
- const github = await this.authenticationService.apis.github;
448
- if (github == null) return undefined;
449
-
450
- const requestedStates: PullRequestStateFilter[] =
451
- options?.state != null && options.state.length > 0 ? [...new Set(options.state)] : ['open'];
452
- const facets =
453
- explicitFilters?.length != null
454
- ? requestedStates.flatMap(state =>
455
- explicitFilters.map(filter => ({
456
- key: `${state}:${filter}`,
457
- state: state,
458
- search: pullRequestRelationshipQualifier[filter],
459
- })),
460
- )
461
- : requestedStates.flatMap(state => [
462
- { key: state, state: state, search: undefined },
463
- ...(options?.includeReviewRequested === true
464
- ? [
465
- {
466
- key: `${state}:${PullRequestFilter.ReviewRequested}`,
467
- state: state,
468
- search: pullRequestRelationshipQualifier[PullRequestFilter.ReviewRequested],
469
- },
470
- ]
471
- : []),
472
- ]);
473
- const cursors = parsePullRequestFacetCursor(options?.cursor);
474
- const hasResumableFacetCursor = Object.keys(cursors).length !== 0;
475
- const facetsWithCursor = facets.filter(facet => cursors[facet.key] != null);
476
- // The first call has no cursor, so query every requested state. A continuation only happens after a
477
- // prior page reported `more:true`, whose bundle carries a cursor for each facet still in flight;
478
- // facets absent from the bundle are exhausted, so re-querying them from scratch would refetch the
479
- // same PRs (duplicated by the dedup-free sweep) and waste an API call per page. Query only the
480
- // states that still have a cursor, but degrade a malformed/empty cursor bundle, or one that doesn't
481
- // apply to the current requested states, to the first page rather than returning an empty page.
482
- const facetsToQuery =
483
- options?.cursor != null && hasResumableFacetCursor && facetsWithCursor.length !== 0
484
- ? facetsWithCursor
485
- : facets;
486
- const results = await Promise.allSettled(
487
- facetsToQuery.map(async facet => ({
488
- key: facet.key,
489
- result: await github.searchMyPullRequestsPage(this, toTokenWithInfo(this.id, session), {
490
- baseUrl: this.apiBaseUrl,
491
- state: facet.state,
492
- cursor: cursors[facet.key],
493
- summary: options?.summary,
494
- ...(facet.search != null
495
- ? { search: facet.search, includeDefaultInvolvement: false }
496
- : undefined),
497
- }),
498
- })),
499
- );
500
- if (results.every(result => result.status === 'rejected')) {
501
- const first = results[0];
502
- if (first?.status === 'rejected') throw first.reason;
503
- }
428
+ // Every account-wide read goes through our own facet search there is deliberately no SDK
429
+ // `getPullRequestsForUser` fallback. It could express neither a state set, nor exact relationships (its
430
+ // `involves:` is a different question), nor the full projection, so which reads it could serve depended on
431
+ // the options, and the two routes disagreed on more than they had to: only this one applies the Launchpad
432
+ // ignored/included repository and organization qualifiers, so the same read returned different pull
433
+ // requests depending on options that were supposed to affect only projection or relationship. With no
434
+ // state and no filters this emits a single facet with no relationship qualifier, which is GitHub's own
435
+ // `involves:@me` and `is:open` default the same question the fallback asked, now on one route.
436
+ const github = await this.authenticationService.apis.github;
437
+ if (github == null) return undefined;
438
+
439
+ const requestedStates: PullRequestStateFilter[] =
440
+ options?.state != null && options.state.length > 0 ? [...new Set(options.state)] : ['open'];
441
+ const facets =
442
+ explicitFilters?.length != null
443
+ ? requestedStates.flatMap(state =>
444
+ explicitFilters.map(filter => ({
445
+ key: `${state}:${filter}`,
446
+ state: state,
447
+ search: gitHubPullRequestRelationshipQualifiers[filter],
448
+ })),
449
+ )
450
+ : requestedStates.flatMap(state => [
451
+ { key: state, state: state, search: undefined },
452
+ ...(options?.includeReviewRequested === true
453
+ ? [
454
+ {
455
+ key: `${state}:${PullRequestFilter.ReviewRequested}`,
456
+ state: state,
457
+ search: gitHubPullRequestRelationshipQualifiers[
458
+ PullRequestFilter.ReviewRequested
459
+ ],
460
+ },
461
+ ]
462
+ : []),
463
+ ]);
464
+ const cursors = parsePullRequestFacetCursor(options?.cursor);
465
+ const hasResumableFacetCursor = Object.keys(cursors).length !== 0;
466
+ const facetsWithCursor = facets.filter(facet => cursors[facet.key] != null);
467
+ // The first call has no cursor, so query every requested state. A continuation only happens after a
468
+ // prior page reported `more:true`, whose bundle carries a cursor for each facet still in flight;
469
+ // facets absent from the bundle are exhausted, so re-querying them from scratch would refetch the
470
+ // same PRs (duplicated by the dedup-free sweep) and waste an API call per page. Query only the
471
+ // states that still have a cursor, but degrade a malformed/empty cursor bundle, or one that doesn't
472
+ // apply to the current requested states, to the first page rather than returning an empty page.
473
+ const facetsToQuery =
474
+ options?.cursor != null && hasResumableFacetCursor && facetsWithCursor.length !== 0
475
+ ? facetsWithCursor
476
+ : facets;
477
+ const results = await Promise.allSettled(
478
+ facetsToQuery.map(async facet => ({
479
+ key: facet.key,
480
+ result: await github.searchMyPullRequestsPage(this, toTokenWithInfo(this.id, session), {
481
+ baseUrl: this.apiBaseUrl,
482
+ state: facet.state,
483
+ cursor: cursors[facet.key],
484
+ summary: options?.summary,
485
+ ...(facet.search != null ? { search: facet.search, includeDefaultInvolvement: false } : undefined),
486
+ }),
487
+ })),
488
+ );
489
+ if (results.every(result => result.status === 'rejected')) {
490
+ const first = results[0];
491
+ if (first?.status === 'rejected') throw first.reason;
492
+ }
504
493
 
505
- const values = new Map<string, ProviderPullRequest>();
506
- const nextCursors: GitHubPullRequestFacetCursor = {};
507
- const failures = [];
508
- let hasMore = false;
509
- let truncated = false;
510
- let structuralIncompleteness = false;
511
- let unkeyedPullRequest = 0;
512
- for (const outcome of results) {
513
- if (outcome.status === 'rejected') {
514
- failures.push(toCollectionScopeFailure({ providerId: this.id }, outcome.reason));
515
- truncated = true;
516
- continue;
517
- }
494
+ const values = new Map<string, ProviderPullRequest>();
495
+ const nextCursors: GitHubPullRequestFacetCursor = {};
496
+ const failures = [];
497
+ let hasMore = false;
498
+ let truncated = false;
499
+ let structuralIncompleteness = false;
500
+ let unkeyedPullRequest = 0;
501
+ for (const outcome of results) {
502
+ if (outcome.status === 'rejected') {
503
+ failures.push(toCollectionScopeFailure({ providerId: this.id }, outcome.reason));
504
+ truncated = true;
505
+ continue;
506
+ }
518
507
 
519
- const { key, result } = outcome.value;
520
- for (const pr of result.values) {
521
- const mapped = toProviderPullRequest(pr);
522
- const identity = getProviderPullRequestIdentity(mapped) ?? `unkeyed:${unkeyedPullRequest++}`;
523
- if (!values.has(identity)) {
524
- values.set(identity, mapped);
525
- }
526
- }
527
- if (result.hasMore) {
528
- if (result.cursor == null || result.cursor === '{}' || result.cursor === cursors[key]) {
529
- truncated = true;
530
- structuralIncompleteness = true;
531
- } else {
532
- hasMore = true;
533
- nextCursors[key] = result.cursor;
534
- }
508
+ const { key, result } = outcome.value;
509
+ for (const pr of result.values) {
510
+ const mapped = toProviderPullRequest(pr);
511
+ const identity = getProviderPullRequestIdentity(mapped) ?? `unkeyed:${unkeyedPullRequest++}`;
512
+ if (!values.has(identity)) {
513
+ values.set(identity, mapped);
535
514
  }
536
- if (result.truncated) {
515
+ }
516
+ if (result.hasMore) {
517
+ if (result.cursor == null || result.cursor === '{}' || result.cursor === cursors[key]) {
537
518
  truncated = true;
519
+ structuralIncompleteness = true;
520
+ } else {
521
+ hasMore = true;
522
+ nextCursors[key] = result.cursor;
538
523
  }
539
524
  }
540
-
541
- return {
542
- values: [...values.values()],
543
- paging: {
544
- more: hasMore,
545
- cursor: hasMore ? JSON.stringify({ type: 'cursor', cursors: nextCursors }) : '{}',
546
- truncated: truncated || undefined,
547
- },
548
- ...(failures.length || structuralIncompleteness
549
- ? {
550
- metadata: {
551
- completeness: 'partial' as const,
552
- ...(failures.length ? { failures: failures } : {}),
553
- },
554
- }
555
- : undefined),
556
- };
525
+ if (result.truncated) {
526
+ truncated = true;
527
+ }
557
528
  }
558
529
 
559
- // The current user's login scopes the account-wide `involves:` query (see getPullRequestsForUser →
560
- // getPullRequestsAssociatedWithUser). Resolve it from THIS session (multi-account safe).
561
- const username = (await this.getProviderCurrentAccount(session))?.username;
562
- if (username == null) return undefined;
563
-
564
- const api = await this.getProvidersApi();
565
- // Only reachable with NO requested state (the branch above owns every state-filtered read), so there is
566
- // deliberately no `states` to forward: the SDK's `involves:` search applies its own `is:open` default
567
- // qualifier, which is exactly the unfiltered "my open PRs" this path is for. Nor could a state be
568
- // honored here — `getPullRequestsAssociatedWithUser` doesn't accept one — which is why the state-filtered
569
- // read goes through `searchMyPullRequestsPage` instead of post-filtering a page that never contained the
570
- // requested states.
571
- const result = await api.getPullRequestsForUser(toTokenWithInfo(this.id, session), username, {
572
- baseUrl: this.apiBaseUrl,
573
- cursor: options?.cursor,
574
- // The sweep drains this read page by page, so it asks for the largest page the search allows.
575
- pageSize: githubSearchMaxPageSize,
576
- });
577
- return result ?? undefined;
530
+ return {
531
+ values: [...values.values()],
532
+ paging: {
533
+ more: hasMore,
534
+ cursor: hasMore ? JSON.stringify({ type: 'cursor', cursors: nextCursors }) : '{}',
535
+ truncated: truncated || undefined,
536
+ },
537
+ ...(failures.length || structuralIncompleteness
538
+ ? {
539
+ metadata: {
540
+ completeness: 'partial' as const,
541
+ ...(failures.length ? { failures: failures } : {}),
542
+ },
543
+ }
544
+ : undefined),
545
+ };
578
546
  }
579
547
 
580
548
  protected override async searchProviderMyIssues(
@@ -719,6 +687,28 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
719
687
  );
720
688
  }
721
689
 
690
+ /**
691
+ * Counts several pull-request scopes in ONE request. Like {@link countProviderIssues}, GitHub's `search`
692
+ * reports `issueCount` on a zero-node selection, so a count preview costs no pull-request transfer.
693
+ */
694
+ protected override async countProviderPullRequests(
695
+ session: ProviderAuthenticationSession,
696
+ scopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: PullRequestSearchCriteria }[],
697
+ cancellation?: AbortSignal,
698
+ ): Promise<(number | undefined)[] | undefined> {
699
+ return (await this.authenticationService.apis.github)?.countPullRequests(
700
+ this,
701
+ toTokenWithInfo(this.id, session),
702
+ scopes.map(s => ({
703
+ repos: s.repos?.map(r => `${r.namespace}/${r.name}`),
704
+ org: s.org,
705
+ criteria: s.criteria,
706
+ })),
707
+ { baseUrl: this.apiBaseUrl },
708
+ cancellation,
709
+ );
710
+ }
711
+
722
712
  protected override async searchProviderPullRequests(
723
713
  session: ProviderAuthenticationSession,
724
714
  searchQuery: string,
@@ -739,18 +729,100 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
739
729
  );
740
730
  }
741
731
 
732
+ /**
733
+ * Stack membership for every stacked pull request in the repository, keyed by pull request number.
734
+ *
735
+ * List surfaces get their pull requests from the shared providers API, whose type carries no stack
736
+ * membership, so the `stack`/`stackEntry` fields selected on the native per-pull-request reads never
737
+ * reach them. This fills that gap in a single request for the whole repository, rather than a
738
+ * per-pull-request enrichment that would scale with the list.
739
+ *
740
+ * `undefined` means unavailable (not enrolled in the preview, or not connected) as distinct from an
741
+ * empty map, which means the repository genuinely has no stacks.
742
+ */
743
+ override async getStacksByPullRequestNumber(
744
+ owner: string,
745
+ repo: string,
746
+ cancellation?: AbortSignal,
747
+ ): Promise<Map<number, PullRequestStackInfo> | undefined> {
748
+ // The shared read path — it refreshes an expired session before use, which a bare `getSession()`
749
+ // does not (it returns the cached session verbatim once one exists, expired or not).
750
+ const session = await this.resolveReadSession(undefined, undefined);
751
+ if (session == null) return undefined;
752
+
753
+ const stacks = await (
754
+ await this.authenticationService.apis.github
755
+ )?.getRepositoryStacks(
756
+ this,
757
+ toTokenWithInfo(this.id, session),
758
+ owner,
759
+ repo,
760
+ {
761
+ baseUrl: this.apiBaseUrl,
762
+ },
763
+ cancellation,
764
+ );
765
+ if (stacks == null) return undefined;
766
+
767
+ const byNumber = new Map<number, PullRequestStackInfo>();
768
+ for (const stack of stacks) {
769
+ // Defensive: this is a public-preview payload reaching us through an unvalidated cast, and a
770
+ // malformed entry must cost its own stack's badges, not the whole panel.
771
+ const members = stack?.pull_requests;
772
+ const baseRef = stack?.base?.ref;
773
+ if (members == null || baseRef == null) continue;
774
+
775
+ // `pull_requests` is ordered bottom to top, and `position` is 1-based from the bottom.
776
+ members.forEach((pr, i) => {
777
+ byNumber.set(pr.number, {
778
+ id: stack.id,
779
+ number: stack.number,
780
+ size: members.length,
781
+ position: i + 1,
782
+ baseRef: baseRef,
783
+ });
784
+ });
785
+ }
786
+
787
+ return byNumber;
788
+ }
789
+
742
790
  protected override async mergeProviderPullRequest(
743
791
  session: ProviderAuthenticationSession,
744
792
  pr: PullRequest,
745
793
  options?: {
746
794
  mergeMethod?: PullRequestMergeMethod;
747
795
  },
796
+ cancellation?: AbortSignal,
748
797
  ): Promise<boolean> {
749
798
  const id = pr.nodeId;
750
799
  const headRefSha = pr.refs?.head?.sha;
751
800
  if (id == null || headRefSha == null) return false;
801
+
802
+ const api = await this.authenticationService.apis.github;
803
+
804
+ // GitHub rejects stacked pull requests on the legacy merge mutation, so they take the
805
+ // asynchronous merge instead — which also lands every layer below this one.
806
+ if (pr.stack != null) {
807
+ return (
808
+ (await api?.mergeStackedPullRequest(
809
+ this,
810
+ toTokenWithInfo(this.id, session),
811
+ pr.repository.owner,
812
+ pr.repository.repo,
813
+ Number(pr.id),
814
+ headRefSha,
815
+ {
816
+ mergeMethod: options?.mergeMethod,
817
+ baseUrl: this.apiBaseUrl,
818
+ },
819
+ cancellation,
820
+ )) ?? false
821
+ );
822
+ }
823
+
752
824
  return (
753
- (await this.authenticationService.apis.github)?.mergePullRequest(
825
+ api?.mergePullRequest(
754
826
  this,
755
827
  toTokenWithInfo(this.id, session),
756
828
  id,
@@ -759,6 +831,7 @@ abstract class GitHubIntegrationBase<ID extends GitHubIntegrationIds> extends Gi
759
831
  mergeMethod: options?.mergeMethod,
760
832
  baseUrl: this.apiBaseUrl,
761
833
  },
834
+ cancellation,
762
835
  ) ?? false
763
836
  );
764
837
  }
@@ -23,6 +23,7 @@ import { toTokenWithInfo } from '../authentication/models.js';
23
23
  import { toCollectionScopeFailure } from '../collectionMetadata.js';
24
24
  import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../constants.js';
25
25
  import type { IntegrationServiceContext } from '../context.js';
26
+ import { IntegrationReadUnavailableError } from '../errors.js';
26
27
  import type { IntegrationConnectionChangeEvent } from '../integrationService.js';
27
28
  import type { SearchMyPullRequestsOptions } from '../models/gitHostIntegration.js';
28
29
  import { GitHostIntegration } from '../models/gitHostIntegration.js';
@@ -64,13 +65,19 @@ const cloudEnterpriseAuthProvider: IntegrationAuthenticationProviderDescriptor =
64
65
  type GitLabPullRequestAssociation = 'assigned' | 'authored' | 'reviewRequested';
65
66
  type GitLabPullRequestFacetCursor = Partial<Record<GitLabPullRequestAssociation, string>>;
66
67
 
67
- const gitLabAssociationForFilter: Record<
68
- Exclude<PullRequestFilter, PullRequestFilter.Mention>,
69
- GitLabPullRequestAssociation
70
- > = {
68
+ /**
69
+ * The account-wide relationships GitLab can express, and the `scope` each maps to. `undefined` means GitLab has
70
+ * no equivalent axis (it has neither `Mention` nor `reviewed-by`), and this map is the single declaration of
71
+ * that. Exhaustive over `PullRequestFilter` on purpose: a new member must be decided for GitLab here rather
72
+ * than compiling through as silently unsupported. Exported for the advertised-vs-expressible capability test,
73
+ * which is what keeps it agreeing with `supportedAccountWidePullRequestFilters`; no runtime consumer imports it.
74
+ */
75
+ export const gitLabAssociationForFilter: Record<PullRequestFilter, GitLabPullRequestAssociation | undefined> = {
71
76
  [PullRequestFilter.Assignee]: 'assigned',
72
77
  [PullRequestFilter.Author]: 'authored',
73
78
  [PullRequestFilter.ReviewRequested]: 'reviewRequested',
79
+ [PullRequestFilter.Reviewed]: undefined,
80
+ [PullRequestFilter.Mention]: undefined,
74
81
  };
75
82
 
76
83
  function parseGitLabPullRequestFacetCursor(cursor: string | undefined): GitLabPullRequestFacetCursor {
@@ -473,15 +480,26 @@ abstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends Gi
473
480
 
474
481
  const api = await this.getProvidersApi();
475
482
  const requested = options?.filters?.length ? new Set(options.filters) : undefined;
483
+ // The filter contract is all-or-nothing, so refuse as soon as ONE requested member is inexpressible rather
484
+ // than only when every one is: dropping the rest would answer a narrower question than was asked
485
+ // (`[Author, Mention]` becoming `authored`). Throw rather than return `undefined`, which the facade reads
486
+ // as "no session could be resolved" and a drain turns into a not-connected warning, misattributing a
487
+ // refusal; `getMyPullRequestsForUserResult` recovers the throw into `{ error }`. Defense in depth:
488
+ // `resolveAccountWidePullRequestFilters` already refuses such a set, so only a direct caller of the public
489
+ // method reaches this.
490
+ const unsupported = requested == null ? [] : [...requested].filter(f => gitLabAssociationForFilter[f] == null);
491
+ if (unsupported.length > 0) {
492
+ throw new IntegrationReadUnavailableError(
493
+ this.name,
494
+ `account-wide pull request filters not expressible by GitLab: ${unsupported.join(', ')}`,
495
+ );
496
+ }
497
+
476
498
  const associations: GitLabPullRequestAssociation[] =
477
499
  requested == null
478
500
  ? ['authored', 'assigned', 'reviewRequested']
479
- : [...requested]
480
- .filter(
481
- (filter): filter is Exclude<PullRequestFilter, PullRequestFilter.Mention> =>
482
- filter !== PullRequestFilter.Mention,
483
- )
484
- .map(filter => gitLabAssociationForFilter[filter]);
501
+ : Array.from(requested, f => gitLabAssociationForFilter[f]).filter(a => a != null);
502
+
485
503
  const cursors = parseGitLabPullRequestFacetCursor(options?.cursor);
486
504
  const resumable = associations.filter(association => cursors[association] != null);
487
505
  const associationsToQuery = options?.cursor != null && resumable.length > 0 ? resumable : associations;
@@ -567,16 +585,8 @@ abstract class GitLabIntegrationBase<ID extends GitLabIntegrationIds> extends Gi
567
585
  }
568
586
 
569
587
  const repoIdsResult = await Promise.allSettled(
570
- repos.map(
571
- (r: GitLabRepositoryDescriptor): Promise<string | undefined> =>
572
- api.getProjectId(
573
- this,
574
- toTokenWithInfo(this.id, session),
575
- r.owner,
576
- r.name,
577
- this.apiBaseUrl,
578
- undefined,
579
- ),
588
+ repos.map((r: GitLabRepositoryDescriptor): Promise<string | undefined> =>
589
+ api.getProjectId(this, toTokenWithInfo(this.id, session), r.owner, r.name, this.apiBaseUrl, undefined),
580
590
  ) ?? [],
581
591
  );
582
592
  const repoInput = repoIdsResult