@gitkraken/core-gitlens 0.5.110 → 0.5.112

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 (362) hide show
  1. package/CHANGELOG.md +19 -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 +38 -2
  18. package/dist/git/models/pullRequest.d.ts.map +1 -1
  19. package/dist/git/models/pullRequest.js +12 -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 +14 -1
  52. package/dist/git/utils/pullRequest.utils.d.ts.map +1 -1
  53. package/dist/git/utils/pullRequest.utils.js +31 -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 +55 -0
  203. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  204. package/dist/plus/git-github/api/github.js +177 -4
  205. package/dist/plus/git-github/api/github.js.map +1 -1
  206. package/dist/plus/git-github/models.d.ts +16 -0
  207. package/dist/plus/git-github/models.d.ts.map +1 -1
  208. package/dist/plus/git-github/models.js +29 -3
  209. package/dist/plus/git-github/models.js.map +1 -1
  210. package/dist/plus/git-github/providers/github/status.d.ts +1 -0
  211. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  212. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  213. package/dist/plus/integrations/context.d.ts +4 -0
  214. package/dist/plus/integrations/context.d.ts.map +1 -1
  215. package/dist/plus/integrations/index.d.ts.map +1 -1
  216. package/dist/plus/integrations/index.js +2 -0
  217. package/dist/plus/integrations/index.js.map +1 -1
  218. package/dist/plus/integrations/models/gitHostIntegration.d.ts +6 -3
  219. package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
  220. package/dist/plus/integrations/models/gitHostIntegration.js +12 -2
  221. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  222. package/dist/plus/integrations/models/integration.d.ts +3 -1
  223. package/dist/plus/integrations/models/integration.d.ts.map +1 -1
  224. package/dist/plus/integrations/models/integration.js +2 -2
  225. package/dist/plus/integrations/models/integration.js.map +1 -1
  226. package/dist/plus/integrations/providers/accounts.d.ts.map +1 -1
  227. package/dist/plus/integrations/providers/accounts.js +4 -0
  228. package/dist/plus/integrations/providers/accounts.js.map +1 -1
  229. package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -1
  230. package/dist/plus/integrations/providers/azure/models.js +3 -0
  231. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  232. package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -1
  233. package/dist/plus/integrations/providers/bitbucket/models.js +1 -0
  234. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  235. package/dist/plus/integrations/providers/github.d.ts +14 -2
  236. package/dist/plus/integrations/providers/github.d.ts.map +1 -1
  237. package/dist/plus/integrations/providers/github.js +55 -3
  238. package/dist/plus/integrations/providers/github.js.map +1 -1
  239. package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
  240. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  241. package/dist/plus/integrations/providers/models.d.ts.map +1 -1
  242. package/dist/plus/integrations/providers/models.js +30 -4
  243. package/dist/plus/integrations/providers/models.js.map +1 -1
  244. package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
  245. package/dist/plus/integrations/reads/broaden.js +6 -1
  246. package/dist/plus/integrations/reads/broaden.js.map +1 -1
  247. package/dist/plus/integrations/reads/drains.d.ts.map +1 -1
  248. package/dist/plus/integrations/reads/drains.js +7 -4
  249. package/dist/plus/integrations/reads/drains.js.map +1 -1
  250. package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
  251. package/dist/plus/integrations/reads/hierarchy.js +3 -1
  252. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  253. package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
  254. package/dist/plus/integrations/reads/issueTracker.js +8 -3
  255. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  256. package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
  257. package/dist/plus/integrations/reads/issues.js +9 -4
  258. package/dist/plus/integrations/reads/issues.js.map +1 -1
  259. package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
  260. package/dist/plus/integrations/reads/pullRequests.js +9 -4
  261. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  262. package/dist/utils/env/browser/base64.d.ts +1 -1
  263. package/dist/utils/env/browser/base64.d.ts.map +1 -1
  264. package/dist/utils/env/node/base64.d.ts +1 -1
  265. package/dist/utils/env/node/base64.d.ts.map +1 -1
  266. package/dist/utils/env/node/base64.js +2 -0
  267. package/dist/utils/env/node/base64.js.map +1 -1
  268. package/dist/utils/keys/chord.d.ts +52 -0
  269. package/dist/utils/keys/chord.d.ts.map +1 -0
  270. package/dist/utils/keys/chord.js +135 -0
  271. package/dist/utils/keys/chord.js.map +1 -0
  272. package/dist/utils/keys/keybinding.d.ts +53 -0
  273. package/dist/utils/keys/keybinding.d.ts.map +1 -0
  274. package/dist/utils/keys/keybinding.js +58 -0
  275. package/dist/utils/keys/keybinding.js.map +1 -0
  276. package/dist/utils/path.d.ts +11 -0
  277. package/dist/utils/path.d.ts.map +1 -1
  278. package/dist/utils/path.js +13 -0
  279. package/dist/utils/path.js.map +1 -1
  280. package/package.json +13 -5
  281. package/src/git/cache.ts +383 -47
  282. package/src/git/context.ts +11 -0
  283. package/src/git/models/fileStatus.ts +1 -0
  284. package/src/git/models/issue.ts +4 -0
  285. package/src/git/models/mergeConflicts.ts +3 -2
  286. package/src/git/models/pullRequest.ts +33 -1
  287. package/src/git/models/repository.ts +58 -17
  288. package/src/git/models/statusFile.ts +3 -0
  289. package/src/git/providers/branches.ts +6 -0
  290. package/src/git/providers/commits.ts +11 -0
  291. package/src/git/providers/config.ts +18 -0
  292. package/src/git/providers/operations.ts +1 -0
  293. package/src/git/providers/pausedOperations.ts +2 -0
  294. package/src/git/providers/status.ts +1 -0
  295. package/src/git/utils/config.utils.ts +8 -0
  296. package/src/git/utils/issue.utils.ts +2 -0
  297. package/src/git/utils/pausedOperationStatus.utils.ts +28 -0
  298. package/src/git/utils/pullRequest.utils.ts +36 -0
  299. package/src/git/watching/changeEvent.ts +6 -1
  300. package/src/git/watching/classifyChange.ts +15 -1
  301. package/src/git/watching/watchService.ts +20 -3
  302. package/src/git/watching/watchSession.ts +41 -6
  303. package/src/git/watching/watcherPatterns.ts +1 -1
  304. package/src/git-cli/exec/git.ts +13 -3
  305. package/src/git-cli/providers/branches.ts +384 -132
  306. package/src/git-cli/providers/commits.ts +106 -15
  307. package/src/git-cli/providers/config.ts +229 -8
  308. package/src/git-cli/providers/contributors.ts +5 -1
  309. package/src/git-cli/providers/graph.ts +26 -13
  310. package/src/git-cli/providers/operations.ts +45 -20
  311. package/src/git-cli/providers/pausedOperations.ts +109 -17
  312. package/src/git-cli/providers/refs.ts +33 -3
  313. package/src/git-cli/providers/status.ts +25 -4
  314. package/src/git-cli/providers/worktrees.ts +5 -0
  315. package/src/git-cli/service.ts +0 -1
  316. package/src/plus/agents/providers/claudeCodeProvider.ts +1130 -87
  317. package/src/plus/agents/providers/claudeCodeTranscript.ts +95 -9
  318. package/src/plus/agents/types.ts +46 -2
  319. package/src/plus/ai/errors.ts +30 -0
  320. package/src/plus/ai/models/model.ts +56 -6
  321. package/src/plus/ai/models/promptTemplates.ts +1 -1
  322. package/src/plus/ai/models/provider.ts +69 -2
  323. package/src/plus/ai/prompts.ts +186 -61
  324. package/src/plus/ai/providers/anthropicProvider.ts +155 -47
  325. package/src/plus/ai/providers/deepSeekProvider.ts +11 -21
  326. package/src/plus/ai/providers/geminiProvider.ts +19 -0
  327. package/src/plus/ai/providers/gitkrakenProvider.ts +126 -3
  328. package/src/plus/ai/providers/huggingFaceProvider.ts +6 -0
  329. package/src/plus/ai/providers/mistralProvider.ts +102 -19
  330. package/src/plus/ai/providers/ollamaProvider.ts +55 -4
  331. package/src/plus/ai/providers/openAICompatibleProvider.ts +5 -0
  332. package/src/plus/ai/providers/openAICompatibleProviderBase.ts +360 -17
  333. package/src/plus/ai/providers/openRouterProvider.ts +17 -0
  334. package/src/plus/ai/providers/openaiProvider.ts +1 -0
  335. package/src/plus/ai/providers/responseFormatCache.ts +39 -0
  336. package/src/plus/ai/providers/xaiProvider.ts +10 -1
  337. package/src/plus/ai/utils/ai.utils.ts +14 -0
  338. package/src/plus/ai/utils/results.utils.ts +255 -31
  339. package/src/plus/git-github/api/github.ts +291 -7
  340. package/src/plus/git-github/models.ts +48 -2
  341. package/src/plus/git-github/providers/github/status.ts +1 -0
  342. package/src/plus/integrations/context.ts +4 -0
  343. package/src/plus/integrations/index.ts +2 -0
  344. package/src/plus/integrations/models/gitHostIntegration.ts +25 -2
  345. package/src/plus/integrations/models/integration.ts +23 -13
  346. package/src/plus/integrations/providers/accounts.ts +4 -0
  347. package/src/plus/integrations/providers/azure/models.ts +3 -0
  348. package/src/plus/integrations/providers/bitbucket/models.ts +1 -0
  349. package/src/plus/integrations/providers/github.ts +85 -1
  350. package/src/plus/integrations/providers/gitlab.ts +2 -10
  351. package/src/plus/integrations/providers/models.ts +36 -4
  352. package/src/plus/integrations/reads/broaden.ts +6 -1
  353. package/src/plus/integrations/reads/drains.ts +7 -4
  354. package/src/plus/integrations/reads/hierarchy.ts +3 -1
  355. package/src/plus/integrations/reads/issueTracker.ts +9 -3
  356. package/src/plus/integrations/reads/issues.ts +9 -4
  357. package/src/plus/integrations/reads/pullRequests.ts +9 -4
  358. package/src/utils/env/browser/base64.ts +1 -1
  359. package/src/utils/env/node/base64.ts +3 -1
  360. package/src/utils/keys/chord.ts +184 -0
  361. package/src/utils/keys/keybinding.ts +116 -0
  362. package/src/utils/path.ts +14 -0
@@ -38,7 +38,7 @@ import {
38
38
  } from '../../../git/utils/revision.utils.js';
39
39
  import { chunk } from '../../../utils/array.js';
40
40
  import { base64 } from '../../../utils/base64.js';
41
- import { CancellationError } from '../../../utils/cancellation.js';
41
+ import { CancellationError, isCancellationError } from '../../../utils/cancellation.js';
42
42
  import { trace } from '../../../utils/decorators/log.js';
43
43
  import type { Event } from '../../../utils/event.js';
44
44
  import { Emitter } from '../../../utils/event.js';
@@ -231,6 +231,85 @@ title
231
231
  updatedAt
232
232
  url
233
233
  `;
234
+ /**
235
+ * Stacked-pull-request selections, appended to a pull request's selection set rather than baked into
236
+ * the fragments below: GitHub Enterprise Server schemas lag github.com, and selecting `stack` there
237
+ * fails the entire query with "Field 'stack' doesn't exist on type 'PullRequest'" — which would take
238
+ * every PR feature down with it, not just stacks. Always add these via `gqlPullRequestStackFragmentFor`.
239
+ */
240
+ const gqlPullRequestStackFragment = `
241
+ stack {
242
+ id
243
+ number
244
+ size
245
+ baseRefName
246
+ }
247
+ stackEntry {
248
+ position
249
+ }
250
+ `;
251
+
252
+ function gqlPullRequestStackFragmentFor(options?: { baseUrl?: string }): string {
253
+ return isGitHubDotCom(options) ? gqlPullRequestStackFragment : '';
254
+ }
255
+
256
+ /** One layer of a stack, as returned by the stacks REST API. Ordered bottom to top. */
257
+ export interface GitHubStackLayer {
258
+ number: number;
259
+ state: string;
260
+ draft?: boolean;
261
+ merged_at?: string | null;
262
+ head: { ref: string; sha: string };
263
+ }
264
+
265
+ export interface GitHubStackResource {
266
+ id: string;
267
+ number: number;
268
+ /** The stack's trunk — what the bottom member targets. */
269
+ base: { ref: string; sha?: string };
270
+ /** False once every member has merged; such a stack can no longer be extended. */
271
+ open?: boolean;
272
+ /** Members ordered bottom to top. */
273
+ pull_requests: GitHubStackLayer[];
274
+ }
275
+
276
+ /** Result of the asynchronous merge used for stacked pull requests. */
277
+ interface GitHubAsyncMergeResult {
278
+ status: 'pending' | 'merged' | 'enqueued' | 'failed';
279
+ /** Everything but `status` is nested here — flattening it silently loses the poll ticket. */
280
+ details?: {
281
+ /** Always present; on `failed` it is the only explanation of why. */
282
+ message?: string;
283
+ /** Only while `pending` — the ticket to poll. */
284
+ uuid?: string;
285
+ /** Only once `merged`. */
286
+ sha?: string;
287
+ merge_method?: string;
288
+ merge_action?: string;
289
+ expected_head_sha?: string;
290
+ };
291
+ }
292
+
293
+ /**
294
+ * A 409 means a merge request is already in flight and carries its ticket, so it resumes rather than fails.
295
+ *
296
+ * The body has to be dug out of `original`: `requestCore` routes every 4xx through `handleRequestError`,
297
+ * which re-wraps octokit's `RequestError` in a `RequestClientError` carrying only `message` and `original`.
298
+ */
299
+ function getAsyncMergeUuidFromConflict(ex: unknown): string | undefined {
300
+ const err = (RequestClientError.is(ex) ? ex.original : ex) as
301
+ | { status?: number; response?: { data?: GitHubAsyncMergeResult } }
302
+ | undefined;
303
+ if (err?.status !== 409) return undefined;
304
+
305
+ return err.response?.data?.details?.uuid;
306
+ }
307
+
308
+ const asyncMergePollIntervalMs = 2000;
309
+ /** A stack merges one layer at a time and GitHub only promises "a few minutes", so the ceiling is
310
+ * generous — giving up early would report a failure for a merge still in progress. */
311
+ const maxAsyncMergePolls = 300;
312
+
234
313
  const gqlPullRequestLiteFragment = `
235
314
  ${gqlIssueOrPullRequestFragment}
236
315
  author {
@@ -295,6 +374,7 @@ assignees(first: 25) {
295
374
  url
296
375
  }
297
376
  }
377
+ changedFiles
298
378
  checksUrl
299
379
  deletions
300
380
  mergeable
@@ -324,6 +404,7 @@ reviewRequests(first: 25) {
324
404
  }
325
405
  }
326
406
  commits(last: 1) {
407
+ totalCount
327
408
  nodes {
328
409
  commit {
329
410
  statusCheckRollup {
@@ -415,6 +496,7 @@ export class GitHubApi {
415
496
 
416
497
  interface QueryResult {
417
498
  viewer: {
499
+ databaseId: number | null;
418
500
  name: string | null;
419
501
  email: string | null;
420
502
  login: string | null;
@@ -425,6 +507,7 @@ export class GitHubApi {
425
507
  try {
426
508
  const query = `query getCurrentAccount($avatarSize: Int) {
427
509
  viewer {
510
+ databaseId
428
511
  name
429
512
  email
430
513
  login
@@ -437,7 +520,10 @@ export class GitHubApi {
437
520
 
438
521
  return {
439
522
  provider: provider,
440
- id: rsp.viewer.login,
523
+ // `id` is the provider's own id, as it is for every other provider, and `username` the handle
524
+ // filters and viewer matching key on. Falls back to the login only when GitHub omits the
525
+ // database id, which leaves the two equal rather than leaving `id` empty.
526
+ id: rsp.viewer.databaseId != null ? String(rsp.viewer.databaseId) : rsp.viewer.login,
441
527
  name: rsp.viewer.name ?? undefined,
442
528
  email: rsp.viewer.email ?? undefined,
443
529
  // If we are GitHub Enterprise, we may need to convert the avatar URL since it might require authentication
@@ -997,6 +1083,7 @@ export class GitHubApi {
997
1083
  repository(name: $repo, owner: $owner) {
998
1084
  pullRequest(number: $number) {
999
1085
  ${gqlPullRequestLiteFragment}
1086
+ ${gqlPullRequestStackFragmentFor(options)}
1000
1087
  }
1001
1088
  }
1002
1089
  }`;
@@ -1077,6 +1164,7 @@ export class GitHubApi {
1077
1164
  associatedPullRequests(first: $limit, orderBy: {field: UPDATED_AT, direction: DESC}, states: $include) {
1078
1165
  nodes {
1079
1166
  ${gqlPullRequestLiteFragment}
1167
+ ${gqlPullRequestStackFragmentFor(options)}
1080
1168
  }
1081
1169
  }
1082
1170
  }
@@ -1170,6 +1258,7 @@ export class GitHubApi {
1170
1258
  associatedPullRequests(first: 2, orderBy: {field: UPDATED_AT, direction: DESC}) {
1171
1259
  nodes {
1172
1260
  ${gqlPullRequestLiteFragment}
1261
+ ${gqlPullRequestStackFragmentFor(options)}
1173
1262
  }
1174
1263
  }
1175
1264
  }
@@ -3307,6 +3396,40 @@ export class GitHubApi {
3307
3396
  scope: ScopedLogger | undefined,
3308
3397
  cancellation?: AbortSignal | undefined,
3309
3398
  ): Promise<Endpoints[R]['response']> {
3399
+ return (await this.requestCore(
3400
+ provider,
3401
+ token,
3402
+ route,
3403
+ options,
3404
+ scope,
3405
+ cancellation,
3406
+ )) as Endpoints[R]['response'];
3407
+ }
3408
+
3409
+ /**
3410
+ * REST call for routes `@octokit/types` doesn't describe yet — currently the stacked-pull-request
3411
+ * merge APIs, which are in public preview and absent from the generated endpoint map. Prefer
3412
+ * `request` for anything typed.
3413
+ */
3414
+ private async requestPreview<T>(
3415
+ provider: Provider | undefined,
3416
+ token: GitHubTokenInfo,
3417
+ route: string,
3418
+ options: RequestParameters | undefined,
3419
+ scope: ScopedLogger | undefined,
3420
+ cancellation?: AbortSignal | undefined,
3421
+ ): Promise<T> {
3422
+ return (await this.requestCore(provider, token, route, options, scope, cancellation)) as T;
3423
+ }
3424
+
3425
+ private async requestCore(
3426
+ provider: Provider | undefined,
3427
+ token: GitHubTokenInfo,
3428
+ route: string,
3429
+ options: RequestParameters | undefined,
3430
+ scope: ScopedLogger | undefined,
3431
+ cancellation?: AbortSignal | undefined,
3432
+ ): Promise<unknown> {
3310
3433
  const { accessToken } = token;
3311
3434
  try {
3312
3435
  let signal: AbortSignal | undefined;
@@ -3320,15 +3443,15 @@ export class GitHubApi {
3320
3443
  // Retry transient gateway/network failures on idempotent reads only
3321
3444
  const method = route.split(' ', 1)[0].toUpperCase();
3322
3445
  const retryable = method === 'GET' || method === 'HEAD';
3323
- return (await this.requestWithRetries(
3446
+ return await this.requestWithRetries(
3324
3447
  () =>
3325
3448
  this.config.wrapForForcedInsecureSSL(provider?.getIgnoreSSLErrors() ?? false, () =>
3326
- this.getDefaults(accessToken, request)(route as string, options),
3449
+ this.getDefaults(accessToken, request)(route, options),
3327
3450
  ),
3328
3451
  retryable,
3329
3452
  signal,
3330
3453
  scope,
3331
- )) as Endpoints[R]['response'];
3454
+ );
3332
3455
  } catch (ex) {
3333
3456
  if (ex instanceof RequestError || ex.name === 'AbortError') {
3334
3457
  this.handleRequestError(provider, token, ex, scope);
@@ -3620,6 +3743,7 @@ export class GitHubApi {
3620
3743
  nodes {
3621
3744
  ...on PullRequest {
3622
3745
  ${options?.summary ? gqlPullRequestLiteFragment : gqlPullRequestFragment}
3746
+ ${gqlPullRequestStackFragmentFor(options)}
3623
3747
  }
3624
3748
  }
3625
3749
  }
@@ -4503,6 +4627,7 @@ export class GitHubApi {
4503
4627
  nodes {
4504
4628
  ... on PullRequest {
4505
4629
  ${gqlPullRequestFragment}
4630
+ ${gqlPullRequestStackFragmentFor(options)}
4506
4631
  }
4507
4632
  }
4508
4633
  }`,
@@ -4655,6 +4780,7 @@ export class GitHubApi {
4655
4780
  nodes {
4656
4781
  ...on PullRequest {
4657
4782
  ${gqlPullRequestFragment}
4783
+ ${gqlPullRequestStackFragmentFor(options)}
4658
4784
  }
4659
4785
  }
4660
4786
  }
@@ -4714,6 +4840,164 @@ export class GitHubApi {
4714
4840
  }
4715
4841
  }
4716
4842
 
4843
+ /**
4844
+ * Every stack in the repository, each with its members bottom to top.
4845
+ *
4846
+ * One request regardless of how many pull requests are involved — which is what makes it the right
4847
+ * shape for list surfaces, whose pull requests arrive through the shared providers API and so carry no
4848
+ * stack membership of their own. Callers join the result by pull request number.
4849
+ *
4850
+ * A `404` means the repository isn't enrolled in the stacked-pull-requests preview; that's reported as
4851
+ * `undefined` rather than an error, since "no stacks" and "not available" both mean nothing to show.
4852
+ */
4853
+ @trace({
4854
+ args: (provider, token, owner, repo) => ({
4855
+ provider: provider.name,
4856
+ token: `<token:${token.microHash}>`,
4857
+ owner: owner,
4858
+ repo: repo,
4859
+ }),
4860
+ })
4861
+ async getRepositoryStacks(
4862
+ provider: Provider,
4863
+ token: GitHubTokenInfo,
4864
+ owner: string,
4865
+ repo: string,
4866
+ options?: { baseUrl?: string },
4867
+ cancellation?: AbortSignal,
4868
+ ): Promise<GitHubStackResource[] | undefined> {
4869
+ const scope = getScopedLogger();
4870
+
4871
+ try {
4872
+ const rsp = await this.requestPreview<{ data: GitHubStackResource[] }>(
4873
+ provider,
4874
+ token,
4875
+ 'GET /repos/{owner}/{repo}/stacks',
4876
+ { owner: owner, repo: repo, baseUrl: options?.baseUrl },
4877
+ scope,
4878
+ cancellation,
4879
+ );
4880
+
4881
+ return rsp.data;
4882
+ } catch (ex) {
4883
+ if (isCancellationError(ex)) throw ex;
4884
+
4885
+ Logger.warn(scope, `Unable to list stacks for ${owner}/${repo}: ${ex}`);
4886
+ return undefined;
4887
+ }
4888
+ }
4889
+
4890
+ /**
4891
+ * Merges a stacked pull request, and with it every layer below it.
4892
+ *
4893
+ * Stacks cannot go through `mergePullRequest` — GitHub rejects stacked pull requests on the legacy
4894
+ * synchronous merge endpoints and mutations. The replacement is asynchronous: submit, then poll a
4895
+ * ticket until it reaches a terminal state.
4896
+ */
4897
+ @trace({
4898
+ args: (provider, token, owner, repo, pullNumber, expectedSourceSha) => ({
4899
+ provider: provider.name,
4900
+ token: `<token:${token.microHash}>`,
4901
+ owner: owner,
4902
+ repo: repo,
4903
+ pullNumber: pullNumber,
4904
+ expectedSourceSha: expectedSourceSha,
4905
+ }),
4906
+ })
4907
+ async mergeStackedPullRequest(
4908
+ provider: Provider,
4909
+ token: GitHubTokenInfo,
4910
+ owner: string,
4911
+ repo: string,
4912
+ pullNumber: number,
4913
+ expectedSourceSha: string,
4914
+ options?: { mergeMethod?: PullRequestMergeMethod; baseUrl?: string },
4915
+ cancellation?: AbortSignal,
4916
+ ): Promise<boolean> {
4917
+ const scope = getScopedLogger();
4918
+
4919
+ try {
4920
+ let uuid: string | undefined;
4921
+
4922
+ try {
4923
+ const submitted = await this.requestPreview<{ data: GitHubAsyncMergeResult }>(
4924
+ provider,
4925
+ token,
4926
+ 'PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge-async',
4927
+ {
4928
+ owner: owner,
4929
+ repo: repo,
4930
+ pull_number: pullNumber,
4931
+ sha: expectedSourceSha,
4932
+ merge_method: options?.mergeMethod,
4933
+ baseUrl: options?.baseUrl,
4934
+ },
4935
+ scope,
4936
+ cancellation,
4937
+ );
4938
+
4939
+ // Already merged or rejected outright — nothing to poll for. `enqueued` lands via the merge
4940
+ // queue, which can take several merge groups to settle, so it's transitional like `pending`.
4941
+ if (submitted.data.status !== 'pending' && submitted.data.status !== 'enqueued') {
4942
+ if (submitted.data.status === 'failed') {
4943
+ Logger.warn(
4944
+ scope,
4945
+ `Stacked merge refused: ${submitted.data.details?.message ?? 'no reason given'}`,
4946
+ );
4947
+ }
4948
+ return submitted.data.status === 'merged';
4949
+ }
4950
+
4951
+ uuid = submitted.data.details?.uuid;
4952
+ } catch (ex) {
4953
+ // A merge request is already in flight (a retry, or a double-click) — adopt its ticket and
4954
+ // poll that instead of reporting a failure for a merge that is actually running.
4955
+ uuid = getAsyncMergeUuidFromConflict(ex);
4956
+ if (uuid == null) throw ex;
4957
+ }
4958
+
4959
+ if (uuid == null) return false;
4960
+
4961
+ // The stack merges server-side one layer at a time, so this can take a while. Poll on a
4962
+ // fixed interval and give up rather than hang forever if the ticket never settles.
4963
+ for (let attempt = 0; attempt < maxAsyncMergePolls; attempt++) {
4964
+ if (cancellation?.aborted) throw new CancellationError();
4965
+
4966
+ await new Promise(resolve => setTimeout(resolve, asyncMergePollIntervalMs));
4967
+
4968
+ const polled = await this.requestPreview<{ data: GitHubAsyncMergeResult }>(
4969
+ provider,
4970
+ token,
4971
+ 'GET /repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid}',
4972
+ {
4973
+ owner: owner,
4974
+ repo: repo,
4975
+ pull_number: pullNumber,
4976
+ uuid: uuid,
4977
+ baseUrl: options?.baseUrl,
4978
+ },
4979
+ scope,
4980
+ cancellation,
4981
+ );
4982
+
4983
+ if (polled.data.status === 'pending' || polled.data.status === 'enqueued') continue;
4984
+
4985
+ if (polled.data.status === 'failed') {
4986
+ Logger.warn(scope, `Stacked merge failed: ${polled.data.details?.message ?? 'no reason given'}`);
4987
+ }
4988
+ return polled.data.status === 'merged';
4989
+ }
4990
+
4991
+ Logger.warn(scope, `Timed out waiting for stacked merge of ${owner}/${repo}#${pullNumber}`);
4992
+ return false;
4993
+ } catch (ex) {
4994
+ if (isCancellationError(ex)) throw ex;
4995
+
4996
+ Logger.error(ex, scope);
4997
+ throw this.handleException(ex, provider, scope);
4998
+ }
4999
+ }
5000
+
4717
5001
  @trace({
4718
5002
  args: (provider, token, nodeId, expectedSourceSha) => ({
4719
5003
  provider: provider.name,
@@ -4732,7 +5016,7 @@ export class GitHubApi {
4732
5016
  ): Promise<boolean> {
4733
5017
  const scope = getScopedLogger();
4734
5018
  interface QueryResult {
4735
- pullRequest: GitHubPullRequestLite | null | undefined;
5019
+ mergePullRequest: { pullRequest: { id: string } | null | undefined } | null | undefined;
4736
5020
  }
4737
5021
 
4738
5022
  let githubMergeStrategy;
@@ -4780,7 +5064,7 @@ export class GitHubApi {
4780
5064
  cancellation,
4781
5065
  );
4782
5066
 
4783
- return rsp?.pullRequest?.id === nodeId;
5067
+ return rsp?.mergePullRequest?.pullRequest?.id === nodeId;
4784
5068
  } catch (ex) {
4785
5069
  throw this.handleException(ex, provider, scope);
4786
5070
  }
@@ -2,7 +2,7 @@ import type { Endpoints } from '@octokit/types';
2
2
  import { GitFileIndexStatus } from '../../git/models/fileStatus.js';
3
3
  import type { IssueLabel } from '../../git/models/issue.js';
4
4
  import { Issue, RepositoryAccessLevel } from '../../git/models/issue.js';
5
- import type { PullRequestMember, PullRequestState } from '../../git/models/pullRequest.js';
5
+ import type { PullRequestMember, PullRequestStackInfo, PullRequestState } from '../../git/models/pullRequest.js';
6
6
  import {
7
7
  PullRequest,
8
8
  PullRequestMergeableState,
@@ -165,6 +165,20 @@ export interface GitHubPullRequestLite extends Omit<GitHubIssueOrPullRequest, '_
165
165
  url: string;
166
166
  viewerPermission: GitHubViewerPermission;
167
167
  };
168
+
169
+ /** Only selected against github.com — GitHub Enterprise Server schemas lag and reject the field. */
170
+ stack?: GitHubPullRequestStack | null;
171
+ /** Only selected against github.com — see `stack`. */
172
+ stackEntry?: { position: number } | null;
173
+ }
174
+
175
+ /** GitHub's stacked-pull-request object. Read-only; all stack mutations are REST-only. */
176
+ export interface GitHubPullRequestStack {
177
+ id: string;
178
+ number: number;
179
+ size: number;
180
+ /** The branch the bottom of the stack targets — the stack's trunk. */
181
+ baseRefName: string;
168
182
  }
169
183
 
170
184
  export interface GitHubIssue extends Omit<GitHubIssueOrPullRequest, '__typename'> {
@@ -206,6 +220,7 @@ export interface GitHubPullRequest extends GitHubPullRequestLite {
206
220
  assignees: {
207
221
  nodes: GitHubMember[];
208
222
  };
223
+ changedFiles: number;
209
224
  checksUrl: string;
210
225
  deletions: number;
211
226
  mergeable: GitHubPullRequestMergeableState;
@@ -226,6 +241,7 @@ export interface GitHubPullRequest extends GitHubPullRequestLite {
226
241
  }[];
227
242
  };
228
243
  commits: {
244
+ totalCount: number;
229
245
  nodes: {
230
246
  commit: {
231
247
  statusCheckRollup: {
@@ -250,7 +266,13 @@ export type GitHubViewerPermission =
250
266
  function fromGitHubMemberOrGhost(member: GitHubMember | null | undefined): PullRequestMember {
251
267
  if (member == null) return { id: 'ghost', name: 'ghost' };
252
268
 
253
- return { id: member.login, name: member.login, avatarUrl: member.avatarUrl, url: member.url };
269
+ return {
270
+ id: member.login,
271
+ name: member.login,
272
+ username: member.login,
273
+ avatarUrl: member.avatarUrl,
274
+ url: member.url,
275
+ };
254
276
  }
255
277
 
256
278
  export function fromGitHubPullRequestLite(pr: GitHubPullRequestLite, provider: Provider): PullRequest {
@@ -299,6 +321,7 @@ export function fromGitHubPullRequestLite(pr: GitHubPullRequestLite, provider: P
299
321
  isCrossRepository: pr.isCrossRepository,
300
322
  },
301
323
  pr.isDraft,
324
+ // The lite fragment selects nothing between `isDraft` and `stack`.
302
325
  undefined, // additions
303
326
  undefined, // deletions
304
327
  undefined, // commentsCount
@@ -310,10 +333,26 @@ export function fromGitHubPullRequestLite(pr: GitHubPullRequestLite, provider: P
310
333
  undefined, // statusCheckRollupState
311
334
  undefined, // project
312
335
  undefined, // version
336
+ undefined, // commitCount
337
+ fromGitHubPullRequestStack(pr),
338
+ undefined, // filesChanged
313
339
  pr.body ?? undefined,
314
340
  );
315
341
  }
316
342
 
343
+ /** Both fields are absent on GitHub Enterprise Server (never selected) and `null` when unstacked. */
344
+ function fromGitHubPullRequestStack(pr: GitHubPullRequestLite): PullRequestStackInfo | undefined {
345
+ if (pr.stack == null || pr.stackEntry == null) return undefined;
346
+
347
+ return {
348
+ id: pr.stack.id,
349
+ number: pr.stack.number,
350
+ size: pr.stack.size,
351
+ position: pr.stackEntry.position,
352
+ baseRef: pr.stack.baseRefName,
353
+ };
354
+ }
355
+
317
356
  export function fromGitHubIssueOrPullRequestState(state: GitHubPullRequestState): PullRequestState {
318
357
  return state === 'MERGED' ? 'merged' : state === 'CLOSED' ? 'closed' : 'opened';
319
358
  }
@@ -482,6 +521,7 @@ export function fromGitHubPullRequest(pr: GitHubPullRequest, provider: Provider)
482
521
  reviewer: {
483
522
  id: r.requestedReviewer.login,
484
523
  name: r.requestedReviewer.login,
524
+ username: r.requestedReviewer.login,
485
525
  avatarUrl: r.requestedReviewer.avatarUrl,
486
526
  url: r.requestedReviewer.url,
487
527
  },
@@ -498,12 +538,16 @@ export function fromGitHubPullRequest(pr: GitHubPullRequest, provider: Provider)
498
538
  pr.assignees.nodes.map(r => ({
499
539
  id: r.login,
500
540
  name: r.login,
541
+ username: r.login,
501
542
  avatarUrl: r.avatarUrl,
502
543
  url: r.url,
503
544
  })),
504
545
  fromGitHubPullRequestStatusCheckRollupState(pr.commits.nodes?.[0]?.commit.statusCheckRollup?.state),
505
546
  undefined, // project
506
547
  undefined, // version
548
+ pr.commits.totalCount,
549
+ fromGitHubPullRequestStack(pr),
550
+ pr.changedFiles,
507
551
  pr.body ?? undefined,
508
552
  );
509
553
  }
@@ -529,12 +573,14 @@ export function fromGitHubIssue(value: GitHubIssue, provider: Provider): Issue {
529
573
  : {
530
574
  id: value.author.login,
531
575
  name: value.author.login,
576
+ username: value.author.login,
532
577
  avatarUrl: value.author.avatarUrl,
533
578
  url: value.author.url,
534
579
  },
535
580
  value.assignees.nodes.map(assignee => ({
536
581
  id: assignee.login,
537
582
  name: assignee.login,
583
+ username: assignee.login,
538
584
  avatarUrl: assignee.avatarUrl,
539
585
  url: assignee.url,
540
586
  })),
@@ -44,6 +44,7 @@ export class StatusGitSubProvider implements GitStatusSubProvider {
44
44
  unstaged?: boolean;
45
45
  untracked?: boolean;
46
46
  throwOnError?: boolean;
47
+ priority?: GitCommandPriority;
47
48
  },
48
49
  _cancellation?: AbortSignal,
49
50
  ): Promise<boolean> {
@@ -155,6 +155,10 @@ export interface IntegrationCacheProvider {
155
155
  cacheable: Cacheable<Account>,
156
156
  options?: CacheExpiryOptions & { connectionId?: string; etag?: string },
157
157
  ): CacheResult<Account>;
158
+ /** Evicts every cached pull request. A merge invalidates more than the merged pull request alone —
159
+ * a stacked merge lands every layer below it and retargets every layer above — and no caller can
160
+ * name that full set. */
161
+ deletePullRequests(): void;
158
162
  }
159
163
 
160
164
  /**
@@ -73,6 +73,8 @@ const uncachedIntegrationCacheProvider: IntegrationCacheProvider = {
73
73
  getIssueOrPullRequest: (_id, _type, _resource, _integration, loader) => loadUncached(loader),
74
74
  getIssue: (_id, _resource, _integration, loader) => loadUncached(loader),
75
75
  getCurrentAccount: (_integration, loader) => loadUncached(loader),
76
+ // Nothing is cached, so there is nothing to evict.
77
+ deletePullRequests: () => {},
76
78
  };
77
79
 
78
80
  function toIntegrationCacheProvider(cache: IntegrationManagerCacheProvider | undefined): IntegrationCacheProvider {
@@ -7,11 +7,13 @@ import type {
7
7
  PullRequest,
8
8
  PullRequestMergeMethod,
9
9
  PullRequestSearchCriteria,
10
+ PullRequestStackInfo,
10
11
  PullRequestStateFilter,
11
12
  } from '../../../git/models/pullRequest.js';
12
13
  import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata.js';
13
14
  import type { ResourceDescriptor } from '../../../git/models/resourceDescriptor.js';
14
15
  import type { PullRequestUrlIdentity } from '../../../git/utils/pullRequest.utils.js';
16
+ import { isCancellationError } from '../../../utils/cancellation.js';
15
17
  import { gate } from '../../../utils/decorators/gate.js';
16
18
  import { trace } from '../../../utils/decorators/log.js';
17
19
  import { first } from '../../../utils/iterable.js';
@@ -267,6 +269,14 @@ export abstract class GitHostIntegration<
267
269
  connectionId?: string;
268
270
  }): Promise<ProviderRepository | undefined>;
269
271
 
272
+ /** Stack membership for every stacked pull request in the repository, keyed by pull request number.
273
+ * Only hosts with a stacks concept implement this (currently GitHub). */
274
+ getStacksByPullRequestNumber?(
275
+ owner: string,
276
+ repo: string,
277
+ cancellation?: AbortSignal,
278
+ ): Promise<Map<number, PullRequestStackInfo> | undefined>;
279
+
270
280
  protected abstract getProviderDefaultBranch(
271
281
  { accessToken }: ProviderAuthenticationSession,
272
282
  repo: T,
@@ -514,7 +524,11 @@ export abstract class GitHostIntegration<
514
524
  options?: { cursor?: string },
515
525
  ): Promise<ProviderHierarchyResult<ProviderRepository> | undefined>;
516
526
 
517
- async mergePullRequest(pr: PullRequest, options?: { mergeMethod?: PullRequestMergeMethod }): Promise<boolean> {
527
+ async mergePullRequest(
528
+ pr: PullRequest,
529
+ options?: { mergeMethod?: PullRequestMergeMethod },
530
+ cancellation?: AbortSignal,
531
+ ): Promise<boolean> {
518
532
  const scope = getScopedLogger();
519
533
 
520
534
  const connected = this.maybeConnected ?? (await this.isConnected());
@@ -523,12 +537,20 @@ export abstract class GitHostIntegration<
523
537
  await this.refreshSessionIfExpired(scope);
524
538
 
525
539
  try {
526
- const result = await this.mergeProviderPullRequest(this._session!, pr, options);
540
+ const result = await this.mergeProviderPullRequest(this._session!, pr, options, cancellation);
527
541
  this.resetRequestExceptionCount('mergePullRequest');
528
542
  return result;
529
543
  } catch (ex) {
544
+ if (isCancellationError(ex)) return false;
545
+
530
546
  this.handleProviderException('mergePullRequest', ex, { scope: scope });
531
547
  return false;
548
+ } finally {
549
+ // A merge attempt just changed the state of more than this pull request on the server (a
550
+ // stacked merge lands every layer below and retargets every layer above) whether it succeeded
551
+ // or failed partway through — so cached pull requests can't be trusted either way. Without
552
+ // this, ref pills and branch enrichment keep serving the pre-merge object until its expiry.
553
+ this.ctx.cache.deletePullRequests();
532
554
  }
533
555
  }
534
556
 
@@ -536,6 +558,7 @@ export abstract class GitHostIntegration<
536
558
  session: ProviderAuthenticationSession,
537
559
  pr: PullRequest,
538
560
  options?: { mergeMethod?: PullRequestMergeMethod },
561
+ cancellation?: AbortSignal,
539
562
  ): Promise<boolean>;
540
563
 
541
564
  @trace()