@gitkraken/core-gitlens 0.1.0 → 0.3.0

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 (451) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/git/cache.d.ts +49 -9
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +174 -14
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/context.d.ts +11 -2
  7. package/dist/git/context.d.ts.map +1 -1
  8. package/dist/git/context.js.map +1 -1
  9. package/dist/git/models/branch.d.ts +1 -0
  10. package/dist/git/models/branch.d.ts.map +1 -1
  11. package/dist/git/models/branch.js.map +1 -1
  12. package/dist/git/models/commit.d.ts +1 -9
  13. package/dist/git/models/commit.d.ts.map +1 -1
  14. package/dist/git/models/commit.js +4 -9
  15. package/dist/git/models/commit.js.map +1 -1
  16. package/dist/git/models/graph.d.ts +2 -4
  17. package/dist/git/models/graph.d.ts.map +1 -1
  18. package/dist/git/models/reference.d.ts +31 -0
  19. package/dist/git/models/reference.d.ts.map +1 -1
  20. package/dist/git/models/remote.d.ts.map +1 -1
  21. package/dist/git/models/remote.js.map +1 -1
  22. package/dist/git/models/repository.d.ts +20 -3
  23. package/dist/git/models/repository.d.ts.map +1 -1
  24. package/dist/git/models/repository.js +42 -5
  25. package/dist/git/models/repository.js.map +1 -1
  26. package/dist/git/models/search.d.ts +2 -2
  27. package/dist/git/models/search.d.ts.map +1 -1
  28. package/dist/git/models/worktree.d.ts +4 -1
  29. package/dist/git/models/worktree.d.ts.map +1 -1
  30. package/dist/git/models/worktree.js +2 -2
  31. package/dist/git/models/worktree.js.map +1 -1
  32. package/dist/git/parsers/diffParser.d.ts +0 -1
  33. package/dist/git/parsers/diffParser.d.ts.map +1 -1
  34. package/dist/git/parsers/diffParser.js +22 -61
  35. package/dist/git/parsers/diffParser.js.map +1 -1
  36. package/dist/git/providers/branches.d.ts +18 -4
  37. package/dist/git/providers/branches.d.ts.map +1 -1
  38. package/dist/git/providers/commits.d.ts +6 -0
  39. package/dist/git/providers/commits.d.ts.map +1 -1
  40. package/dist/git/providers/config.d.ts +6 -1
  41. package/dist/git/providers/config.d.ts.map +1 -1
  42. package/dist/git/providers/diff.d.ts +2 -0
  43. package/dist/git/providers/diff.d.ts.map +1 -1
  44. package/dist/git/providers/operations.d.ts +39 -10
  45. package/dist/git/providers/operations.d.ts.map +1 -1
  46. package/dist/git/providers/pausedOperations.d.ts +3 -1
  47. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  48. package/dist/git/providers/refs.d.ts +23 -2
  49. package/dist/git/providers/refs.d.ts.map +1 -1
  50. package/dist/git/providers/revision.d.ts +1 -1
  51. package/dist/git/providers/revision.d.ts.map +1 -1
  52. package/dist/git/providers/staging.d.ts +18 -0
  53. package/dist/git/providers/staging.d.ts.map +1 -1
  54. package/dist/git/providers/status.d.ts +4 -1
  55. package/dist/git/providers/status.d.ts.map +1 -1
  56. package/dist/git/remotes/azure-devops.d.ts.map +1 -1
  57. package/dist/git/remotes/azure-devops.js.map +1 -1
  58. package/dist/git/{exec.types.d.ts → run.types.d.ts} +22 -3
  59. package/dist/git/run.types.d.ts.map +1 -0
  60. package/dist/git/run.types.js +2 -0
  61. package/dist/git/run.types.js.map +1 -0
  62. package/dist/git/service.d.ts.map +1 -1
  63. package/dist/git/service.js.map +1 -1
  64. package/dist/git/utils/autolink.utils.d.ts.map +1 -1
  65. package/dist/git/utils/autolink.utils.js.map +1 -1
  66. package/dist/git/utils/blame.utils.d.ts.map +1 -1
  67. package/dist/git/utils/blame.utils.js.map +1 -1
  68. package/dist/git/utils/conflictResolution.utils.d.ts +6 -0
  69. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -0
  70. package/dist/git/utils/conflictResolution.utils.js +25 -0
  71. package/dist/git/utils/conflictResolution.utils.js.map +1 -0
  72. package/dist/git/utils/fileStatus.utils.d.ts +2 -1
  73. package/dist/git/utils/fileStatus.utils.d.ts.map +1 -1
  74. package/dist/git/utils/fileStatus.utils.js +4 -0
  75. package/dist/git/utils/fileStatus.utils.js.map +1 -1
  76. package/dist/git/utils/search.utils.d.ts.map +1 -1
  77. package/dist/git/utils/search.utils.js +3 -0
  78. package/dist/git/utils/search.utils.js.map +1 -1
  79. package/dist/git/utils/sorting.d.ts +14 -0
  80. package/dist/git/utils/sorting.d.ts.map +1 -1
  81. package/dist/git/utils/sorting.js +34 -0
  82. package/dist/git/utils/sorting.js.map +1 -1
  83. package/dist/git/utils/status.utils.d.ts.map +1 -1
  84. package/dist/git/utils/status.utils.js.map +1 -1
  85. package/dist/git/watching/classifyChange.d.ts.map +1 -1
  86. package/dist/git/watching/classifyChange.js +8 -0
  87. package/dist/git/watching/classifyChange.js.map +1 -1
  88. package/dist/git/watching/gitIgnoreFilter.js +1 -1
  89. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  90. package/dist/git/watching/initWatcher.d.ts.map +1 -1
  91. package/dist/git/watching/initWatcher.js.map +1 -1
  92. package/dist/git/watching/watchGroup.d.ts.map +1 -1
  93. package/dist/git/watching/watchGroup.js.map +1 -1
  94. package/dist/git/watching/watchService.d.ts.map +1 -1
  95. package/dist/git/watching/watchService.js +24 -4
  96. package/dist/git/watching/watchService.js.map +1 -1
  97. package/dist/git/watching/watchSession.d.ts.map +1 -1
  98. package/dist/git/watching/watchSession.js.map +1 -1
  99. package/dist/git-cli/cliGitProvider.js +1 -1
  100. package/dist/git-cli/cliGitProvider.js.map +1 -1
  101. package/dist/git-cli/exec/exec.types.d.ts +1 -1
  102. package/dist/git-cli/exec/exec.types.d.ts.map +1 -1
  103. package/dist/git-cli/exec/git.d.ts +31 -6
  104. package/dist/git-cli/exec/git.d.ts.map +1 -1
  105. package/dist/git-cli/exec/git.js +70 -9
  106. package/dist/git-cli/exec/git.js.map +1 -1
  107. package/dist/git-cli/exec/gitQueue.d.ts +22 -6
  108. package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
  109. package/dist/git-cli/exec/gitQueue.js +88 -24
  110. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  111. package/dist/git-cli/parsers/blameParser.d.ts.map +1 -1
  112. package/dist/git-cli/parsers/blameParser.js.map +1 -1
  113. package/dist/git-cli/parsers/logParser.d.ts +1 -0
  114. package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
  115. package/dist/git-cli/parsers/logParser.js +4 -0
  116. package/dist/git-cli/parsers/logParser.js.map +1 -1
  117. package/dist/git-cli/parsers/refParser.d.ts +3 -30
  118. package/dist/git-cli/parsers/refParser.d.ts.map +1 -1
  119. package/dist/git-cli/parsers/refParser.js +30 -30
  120. package/dist/git-cli/parsers/refParser.js.map +1 -1
  121. package/dist/git-cli/providers/blame.d.ts.map +1 -1
  122. package/dist/git-cli/providers/blame.js +1 -1
  123. package/dist/git-cli/providers/blame.js.map +1 -1
  124. package/dist/git-cli/providers/branches.d.ts +17 -5
  125. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  126. package/dist/git-cli/providers/branches.js +245 -162
  127. package/dist/git-cli/providers/branches.js.map +1 -1
  128. package/dist/git-cli/providers/commits.d.ts +4 -0
  129. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  130. package/dist/git-cli/providers/commits.js +120 -35
  131. package/dist/git-cli/providers/commits.js.map +1 -1
  132. package/dist/git-cli/providers/config.d.ts +11 -2
  133. package/dist/git-cli/providers/config.d.ts.map +1 -1
  134. package/dist/git-cli/providers/config.js +36 -11
  135. package/dist/git-cli/providers/config.js.map +1 -1
  136. package/dist/git-cli/providers/contributors.d.ts.map +1 -1
  137. package/dist/git-cli/providers/contributors.js +7 -4
  138. package/dist/git-cli/providers/contributors.js.map +1 -1
  139. package/dist/git-cli/providers/diff.d.ts +2 -0
  140. package/dist/git-cli/providers/diff.d.ts.map +1 -1
  141. package/dist/git-cli/providers/diff.js +92 -20
  142. package/dist/git-cli/providers/diff.js.map +1 -1
  143. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  144. package/dist/git-cli/providers/graph.js +49 -28
  145. package/dist/git-cli/providers/graph.js.map +1 -1
  146. package/dist/git-cli/providers/operations.d.ts +30 -11
  147. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  148. package/dist/git-cli/providers/operations.js +146 -43
  149. package/dist/git-cli/providers/operations.js.map +1 -1
  150. package/dist/git-cli/providers/patch.d.ts.map +1 -1
  151. package/dist/git-cli/providers/patch.js +15 -30
  152. package/dist/git-cli/providers/patch.js.map +1 -1
  153. package/dist/git-cli/providers/pausedOperations.d.ts +3 -1
  154. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  155. package/dist/git-cli/providers/pausedOperations.js +13 -7
  156. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  157. package/dist/git-cli/providers/refs.d.ts +19 -2
  158. package/dist/git-cli/providers/refs.d.ts.map +1 -1
  159. package/dist/git-cli/providers/refs.js +312 -9
  160. package/dist/git-cli/providers/refs.js.map +1 -1
  161. package/dist/git-cli/providers/remotes.js +4 -4
  162. package/dist/git-cli/providers/remotes.js.map +1 -1
  163. package/dist/git-cli/providers/revision.d.ts +1 -1
  164. package/dist/git-cli/providers/revision.d.ts.map +1 -1
  165. package/dist/git-cli/providers/revision.js +15 -11
  166. package/dist/git-cli/providers/revision.js.map +1 -1
  167. package/dist/git-cli/providers/staging.d.ts +8 -0
  168. package/dist/git-cli/providers/staging.d.ts.map +1 -1
  169. package/dist/git-cli/providers/staging.js +52 -8
  170. package/dist/git-cli/providers/staging.js.map +1 -1
  171. package/dist/git-cli/providers/stash.d.ts +0 -8
  172. package/dist/git-cli/providers/stash.d.ts.map +1 -1
  173. package/dist/git-cli/providers/stash.js +63 -119
  174. package/dist/git-cli/providers/stash.js.map +1 -1
  175. package/dist/git-cli/providers/status.d.ts +4 -1
  176. package/dist/git-cli/providers/status.d.ts.map +1 -1
  177. package/dist/git-cli/providers/status.js +10 -9
  178. package/dist/git-cli/providers/status.js.map +1 -1
  179. package/dist/git-cli/providers/tags.d.ts.map +1 -1
  180. package/dist/git-cli/providers/tags.js +13 -9
  181. package/dist/git-cli/providers/tags.js.map +1 -1
  182. package/dist/git-cli/providers/worktrees.js +3 -3
  183. package/dist/git-cli/providers/worktrees.js.map +1 -1
  184. package/dist/ipc/discovery.d.ts +27 -0
  185. package/dist/ipc/discovery.d.ts.map +1 -0
  186. package/dist/ipc/discovery.js +38 -0
  187. package/dist/ipc/discovery.js.map +1 -0
  188. package/dist/ipc/ipcServer.d.ts +20 -0
  189. package/dist/ipc/ipcServer.d.ts.map +1 -0
  190. package/dist/ipc/ipcServer.js +162 -0
  191. package/dist/ipc/ipcServer.js.map +1 -0
  192. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +114 -0
  193. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -0
  194. package/dist/plus/agents/providers/claudeCodeProvider.js +1480 -0
  195. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -0
  196. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +36 -0
  197. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -0
  198. package/dist/plus/agents/providers/claudeCodeTranscript.js +171 -0
  199. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -0
  200. package/dist/plus/agents/sanitizePrompt.d.ts +15 -0
  201. package/dist/plus/agents/sanitizePrompt.d.ts.map +1 -0
  202. package/dist/plus/agents/sanitizePrompt.js +103 -0
  203. package/dist/plus/agents/sanitizePrompt.js.map +1 -0
  204. package/dist/plus/agents/stateMachine.d.ts +32 -0
  205. package/dist/plus/agents/stateMachine.d.ts.map +1 -0
  206. package/dist/plus/agents/stateMachine.js +180 -0
  207. package/dist/plus/agents/stateMachine.js.map +1 -0
  208. package/dist/plus/agents/types.d.ts +203 -0
  209. package/dist/plus/agents/types.d.ts.map +1 -0
  210. package/dist/plus/agents/types.js +49 -0
  211. package/dist/plus/agents/types.js.map +1 -0
  212. package/dist/plus/ai/constants.d.ts +2 -1
  213. package/dist/plus/ai/constants.d.ts.map +1 -1
  214. package/dist/plus/ai/constants.js +7 -0
  215. package/dist/plus/ai/constants.js.map +1 -1
  216. package/dist/plus/ai/errors.d.ts +4 -1
  217. package/dist/plus/ai/errors.d.ts.map +1 -1
  218. package/dist/plus/ai/errors.js +38 -0
  219. package/dist/plus/ai/errors.js.map +1 -1
  220. package/dist/plus/ai/models/model.d.ts +1 -1
  221. package/dist/plus/ai/models/model.d.ts.map +1 -1
  222. package/dist/plus/ai/models/model.js.map +1 -1
  223. package/dist/plus/ai/models/promptTemplates.d.ts +28 -2
  224. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  225. package/dist/plus/ai/models/results.d.ts +28 -0
  226. package/dist/plus/ai/models/results.d.ts.map +1 -1
  227. package/dist/plus/ai/prompts.d.ts +4 -0
  228. package/dist/plus/ai/prompts.d.ts.map +1 -1
  229. package/dist/plus/ai/prompts.js +167 -1
  230. package/dist/plus/ai/prompts.js.map +1 -1
  231. package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
  232. package/dist/plus/ai/utils/ai.utils.js +2 -0
  233. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  234. package/dist/plus/ai/utils/results.utils.d.ts +3 -0
  235. package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
  236. package/dist/plus/ai/utils/results.utils.js +75 -0
  237. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  238. package/dist/plus/ai/utils/truncation.utils.d.ts +1 -1
  239. package/dist/plus/ai/utils/truncation.utils.d.ts.map +1 -1
  240. package/dist/plus/ai/utils/truncation.utils.js.map +1 -1
  241. package/dist/plus/git-github/api/github.d.ts +10 -1
  242. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  243. package/dist/plus/git-github/api/github.js +156 -49
  244. package/dist/plus/git-github/api/github.js.map +1 -1
  245. package/dist/plus/git-github/api/types.d.ts +8 -0
  246. package/dist/plus/git-github/api/types.d.ts.map +1 -1
  247. package/dist/plus/git-github/models.d.ts +1 -1
  248. package/dist/plus/git-github/models.d.ts.map +1 -1
  249. package/dist/plus/git-github/models.js.map +1 -1
  250. package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
  251. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  252. package/dist/plus/git-github/providers/github/branches.d.ts +5 -1
  253. package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
  254. package/dist/plus/git-github/providers/github/branches.js +60 -22
  255. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  256. package/dist/plus/git-github/providers/github/commits.d.ts.map +1 -1
  257. package/dist/plus/git-github/providers/github/commits.js +144 -92
  258. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  259. package/dist/plus/git-github/providers/github/diff.d.ts +3 -1
  260. package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
  261. package/dist/plus/git-github/providers/github/diff.js +23 -9
  262. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  263. package/dist/plus/git-github/providers/github/refs.d.ts +9 -1
  264. package/dist/plus/git-github/providers/github/refs.d.ts.map +1 -1
  265. package/dist/plus/git-github/providers/github/refs.js +38 -13
  266. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  267. package/dist/plus/git-github/providers/github/revision.d.ts.map +1 -1
  268. package/dist/plus/git-github/providers/github/revision.js +1 -2
  269. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  270. package/dist/plus/git-github/providers/github/status.d.ts +4 -1
  271. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  272. package/dist/plus/git-github/providers/github/status.js +1 -1
  273. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  274. package/dist/plus/git-github/providers/github/tags.d.ts.map +1 -1
  275. package/dist/plus/git-github/providers/github/tags.js +35 -36
  276. package/dist/plus/git-github/providers/github/tags.js.map +1 -1
  277. package/dist/plus/git-github/providers/githubGitProvider.d.ts.map +1 -1
  278. package/dist/plus/git-github/providers/githubGitProvider.js.map +1 -1
  279. package/dist/utils/color.d.ts.map +1 -1
  280. package/dist/utils/color.js.map +1 -1
  281. package/dist/utils/decorators/log.js +1 -1
  282. package/dist/utils/decorators/log.js.map +1 -1
  283. package/dist/utils/decorators/sequentialize.d.ts.map +1 -1
  284. package/dist/utils/decorators/sequentialize.js +4 -2
  285. package/dist/utils/decorators/sequentialize.js.map +1 -1
  286. package/dist/utils/decorators/serializable.d.ts.map +1 -1
  287. package/dist/utils/decorators/serializable.js.map +1 -1
  288. package/dist/utils/dedupedAsyncCache.d.ts +37 -0
  289. package/dist/utils/dedupedAsyncCache.d.ts.map +1 -0
  290. package/dist/utils/dedupedAsyncCache.js +68 -0
  291. package/dist/utils/dedupedAsyncCache.js.map +1 -0
  292. package/dist/utils/diff.d.ts +2 -0
  293. package/dist/utils/diff.d.ts.map +1 -0
  294. package/dist/utils/diff.js +53 -0
  295. package/dist/utils/diff.js.map +1 -0
  296. package/dist/utils/disposable.d.ts.map +1 -1
  297. package/dist/utils/disposable.js.map +1 -1
  298. package/dist/utils/env/node/base64.js.map +1 -1
  299. package/dist/utils/env/node/exec.d.ts.map +1 -1
  300. package/dist/utils/env/node/exec.js.map +1 -1
  301. package/dist/utils/error.d.ts.map +1 -1
  302. package/dist/utils/error.js.map +1 -1
  303. package/dist/utils/formatter.d.ts.map +1 -1
  304. package/dist/utils/formatter.js +4 -10
  305. package/dist/utils/formatter.js.map +1 -1
  306. package/dist/utils/iterable.d.ts.map +1 -1
  307. package/dist/utils/iterable.js.map +1 -1
  308. package/dist/utils/logger.d.ts.map +1 -1
  309. package/dist/utils/logger.js.map +1 -1
  310. package/dist/utils/lruMap.d.ts +49 -0
  311. package/dist/utils/lruMap.d.ts.map +1 -0
  312. package/dist/utils/lruMap.js +103 -0
  313. package/dist/utils/lruMap.js.map +1 -0
  314. package/dist/utils/markdown.d.ts.map +1 -1
  315. package/dist/utils/markdown.js.map +1 -1
  316. package/dist/utils/mru.d.ts.map +1 -1
  317. package/dist/utils/mru.js.map +1 -1
  318. package/dist/utils/path.d.ts.map +1 -1
  319. package/dist/utils/path.js.map +1 -1
  320. package/dist/utils/promise.d.ts +1 -0
  321. package/dist/utils/promise.d.ts.map +1 -1
  322. package/dist/utils/promise.js +17 -0
  323. package/dist/utils/promise.js.map +1 -1
  324. package/dist/utils/promiseCache.d.ts +10 -0
  325. package/dist/utils/promiseCache.d.ts.map +1 -1
  326. package/dist/utils/promiseCache.js +22 -0
  327. package/dist/utils/promiseCache.js.map +1 -1
  328. package/dist/utils/string.d.ts.map +1 -1
  329. package/dist/utils/string.js.map +1 -1
  330. package/dist/utils/uri.d.ts.map +1 -1
  331. package/dist/utils/uri.js.map +1 -1
  332. package/dist/utils/version.d.ts +14 -0
  333. package/dist/utils/version.d.ts.map +1 -1
  334. package/dist/utils/version.js +48 -0
  335. package/dist/utils/version.js.map +1 -1
  336. package/package.json +13 -1
  337. package/src/git/cache.ts +309 -29
  338. package/src/git/context.ts +11 -2
  339. package/src/git/models/branch.ts +1 -0
  340. package/src/git/models/commit.ts +2 -17
  341. package/src/git/models/graph.ts +2 -4
  342. package/src/git/models/reference.ts +33 -0
  343. package/src/git/models/remote.ts +1 -0
  344. package/src/git/models/repository.ts +44 -6
  345. package/src/git/models/search.ts +2 -2
  346. package/src/git/models/worktree.ts +10 -2
  347. package/src/git/parsers/diffParser.ts +9 -44
  348. package/src/git/providers/branches.ts +25 -4
  349. package/src/git/providers/commits.ts +7 -0
  350. package/src/git/providers/config.ts +4 -0
  351. package/src/git/providers/diff.ts +8 -2
  352. package/src/git/providers/operations.ts +54 -3
  353. package/src/git/providers/pausedOperations.ts +1 -0
  354. package/src/git/providers/refs.ts +32 -3
  355. package/src/git/providers/revision.ts +5 -1
  356. package/src/git/providers/staging.ts +14 -0
  357. package/src/git/providers/status.ts +6 -1
  358. package/src/git/remotes/azure-devops.ts +2 -0
  359. package/src/git/{exec.types.ts → run.types.ts} +22 -2
  360. package/src/git/service.ts +1 -0
  361. package/src/git/utils/autolink.utils.ts +1 -0
  362. package/src/git/utils/blame.utils.ts +2 -0
  363. package/src/git/utils/conflictResolution.utils.ts +31 -0
  364. package/src/git/utils/fileStatus.utils.ts +7 -1
  365. package/src/git/utils/search.utils.ts +2 -0
  366. package/src/git/utils/sorting.ts +38 -0
  367. package/src/git/utils/status.utils.ts +1 -0
  368. package/src/git/watching/classifyChange.ts +9 -0
  369. package/src/git/watching/gitIgnoreFilter.ts +2 -2
  370. package/src/git/watching/initWatcher.ts +2 -0
  371. package/src/git/watching/watchGroup.ts +3 -0
  372. package/src/git/watching/watchService.ts +38 -5
  373. package/src/git/watching/watchSession.ts +10 -0
  374. package/src/git-cli/cliGitProvider.ts +1 -1
  375. package/src/git-cli/exec/exec.types.ts +2 -2
  376. package/src/git-cli/exec/git.ts +76 -23
  377. package/src/git-cli/exec/gitQueue.ts +98 -25
  378. package/src/git-cli/parsers/blameParser.ts +1 -0
  379. package/src/git-cli/parsers/logParser.ts +6 -1
  380. package/src/git-cli/parsers/refParser.ts +33 -34
  381. package/src/git-cli/providers/blame.ts +2 -1
  382. package/src/git-cli/providers/branches.ts +341 -196
  383. package/src/git-cli/providers/commits.ts +145 -46
  384. package/src/git-cli/providers/config.ts +50 -16
  385. package/src/git-cli/providers/contributors.ts +7 -12
  386. package/src/git-cli/providers/diff.ts +107 -20
  387. package/src/git-cli/providers/graph.ts +58 -29
  388. package/src/git-cli/providers/operations.ts +253 -57
  389. package/src/git-cli/providers/patch.ts +16 -37
  390. package/src/git-cli/providers/pausedOperations.ts +14 -6
  391. package/src/git-cli/providers/refs.ts +274 -11
  392. package/src/git-cli/providers/remotes.ts +4 -4
  393. package/src/git-cli/providers/revision.ts +22 -15
  394. package/src/git-cli/providers/staging.ts +51 -8
  395. package/src/git-cli/providers/stash.ts +67 -134
  396. package/src/git-cli/providers/status.ts +21 -10
  397. package/src/git-cli/providers/tags.ts +18 -19
  398. package/src/git-cli/providers/worktrees.ts +3 -3
  399. package/src/ipc/discovery.ts +56 -0
  400. package/src/ipc/ipcServer.ts +171 -0
  401. package/src/plus/agents/providers/claudeCodeProvider.ts +1782 -0
  402. package/src/plus/agents/providers/claudeCodeTranscript.ts +214 -0
  403. package/src/plus/agents/sanitizePrompt.ts +109 -0
  404. package/src/plus/agents/stateMachine.ts +197 -0
  405. package/src/plus/agents/types.ts +277 -0
  406. package/src/plus/ai/constants.ts +8 -0
  407. package/src/plus/ai/errors.ts +40 -0
  408. package/src/plus/ai/models/model.ts +1 -0
  409. package/src/plus/ai/models/promptTemplates.ts +42 -0
  410. package/src/plus/ai/models/results.ts +30 -0
  411. package/src/plus/ai/prompts.ts +171 -1
  412. package/src/plus/ai/utils/ai.utils.ts +2 -0
  413. package/src/plus/ai/utils/results.utils.ts +101 -0
  414. package/src/plus/ai/utils/truncation.utils.ts +3 -1
  415. package/src/plus/git-github/api/github.ts +199 -53
  416. package/src/plus/git-github/api/types.ts +15 -0
  417. package/src/plus/git-github/models.ts +1 -1
  418. package/src/plus/git-github/providers/github/blame.ts +2 -0
  419. package/src/plus/git-github/providers/github/branches.ts +75 -33
  420. package/src/plus/git-github/providers/github/commits.ts +231 -160
  421. package/src/plus/git-github/providers/github/diff.ts +35 -21
  422. package/src/plus/git-github/providers/github/refs.ts +57 -22
  423. package/src/plus/git-github/providers/github/revision.ts +1 -2
  424. package/src/plus/git-github/providers/github/status.ts +6 -1
  425. package/src/plus/git-github/providers/github/tags.ts +61 -62
  426. package/src/plus/git-github/providers/githubGitProvider.ts +1 -0
  427. package/src/utils/color.ts +2 -0
  428. package/src/utils/decorators/log.ts +1 -1
  429. package/src/utils/decorators/sequentialize.ts +8 -6
  430. package/src/utils/decorators/serializable.ts +1 -0
  431. package/src/utils/dedupedAsyncCache.ts +74 -0
  432. package/src/utils/diff.ts +61 -0
  433. package/src/utils/disposable.ts +2 -0
  434. package/src/utils/env/node/base64.ts +1 -1
  435. package/src/utils/env/node/exec.ts +4 -2
  436. package/src/utils/error.ts +1 -0
  437. package/src/utils/formatter.ts +4 -11
  438. package/src/utils/iterable.ts +4 -1
  439. package/src/utils/logger.ts +1 -0
  440. package/src/utils/lruMap.ts +112 -0
  441. package/src/utils/markdown.ts +1 -0
  442. package/src/utils/mru.ts +2 -0
  443. package/src/utils/path.ts +1 -0
  444. package/src/utils/promise.ts +22 -0
  445. package/src/utils/promiseCache.ts +24 -0
  446. package/src/utils/string.ts +2 -0
  447. package/src/utils/uri.ts +1 -0
  448. package/src/utils/version.ts +49 -0
  449. package/dist/git/exec.types.d.ts.map +0 -1
  450. package/dist/git/exec.types.js +0 -2
  451. package/dist/git/exec.types.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import type { Cache, ConflictDetectionCacheKey } from '../../git/cache.js';
1
+ import type { Cache, ConflictDetectionCacheKey, GkConfigInvalidationTarget } from '../../git/cache.js';
2
2
  import type { GitServiceContext } from '../../git/context.js';
3
3
  import { BranchError } from '../../git/errors.js';
4
4
  import type { BranchDisposition, BranchMetadata } from '../../git/models/branch.js';
@@ -10,6 +10,7 @@ import type {
10
10
  GitBranchesSubProvider,
11
11
  GitBranchMergedStatus,
12
12
  } from '../../git/providers/branches.js';
13
+ import type { GitCommandPriority } from '../../git/run.types.js';
13
14
  import {
14
15
  formatDetachedHeadName,
15
16
  getBranchId,
@@ -41,7 +42,6 @@ import type { GitResult } from '../exec/exec.types.js';
41
42
  import type { Git } from '../exec/git.js';
42
43
  import { getGitCommandError, gitConfigsBranch, gitConfigsLog, GitError, GitErrors, GitWarnings } from '../exec/git.js';
43
44
  import { parseMergeTreeConflict } from '../parsers/mergeTreeParser.js';
44
- import { getBranchParser } from '../parsers/refParser.js';
45
45
 
46
46
  /** Minimum time between writes to the config for last accessed/modified dates */
47
47
  const dateMetadataStaleThresholdMs = 5 * 60 * 1000; // 5 minutes
@@ -96,10 +96,10 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
96
96
  const [pausedOpStatusResult, committerDateResult, defaultWorktreePathResult, metadataMapResult] =
97
97
  await Promise.allSettled([
98
98
  isDetachedHead(ref.name)
99
- ? this.provider.pausedOps?.getPausedOperationStatus(repoPath, cancellation)
99
+ ? this.provider.pausedOps?.getPausedOperationStatus(repoPath, undefined, cancellation)
100
100
  : undefined,
101
101
  this.git
102
- .exec(
102
+ .run(
103
103
  {
104
104
  cwd: repoPath,
105
105
  cancellation: cancellation,
@@ -168,27 +168,22 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
168
168
  // own cancellation when bypassing the cache (e.g. paging-cursor path).
169
169
  signal ??= cancellation;
170
170
  try {
171
- const supported = await this.git.supported('git:for-each-ref');
172
- const parser = getBranchParser(supported);
173
-
174
- const [gitResult, defaultWorktreePathResult, metadataMapResult] = await Promise.allSettled([
175
- this.git.exec(
176
- { cwd: commonPath, cancellation: signal },
177
- 'for-each-ref',
178
- ...parser.arguments,
179
- 'refs/heads/',
180
- 'refs/remotes/',
181
- ),
182
- this.provider.config.getDefaultWorktreePath?.(commonPath),
183
- this.getBranchMetadataMap(commonPath),
184
- ]);
185
-
186
- const result = getSettledValue(gitResult);
171
+ const [recordsResult, supportedResult, defaultWorktreePathResult, metadataMapResult] =
172
+ await Promise.allSettled([
173
+ this.provider.refs.getRefs(commonPath, signal),
174
+ this.git.supported('git:for-each-ref'),
175
+ this.provider.config.getDefaultWorktreePath?.(commonPath),
176
+ this.getBranchMetadataMap(commonPath),
177
+ ]);
178
+
179
+ const records = getSettledValue(recordsResult) ?? [];
180
+ const supported = getSettledValue(supportedResult) ?? [];
187
181
  const metadataMap = getSettledValue(metadataMapResult) ?? new Map<string, BranchMetadata>();
188
182
  const defaultWorktreePath = getSettledValue(defaultWorktreePathResult);
183
+ const worktreePathSupported = supported.includes('git:for-each-ref:worktreePath');
189
184
 
190
185
  // If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch
191
- if (!result?.stdout) {
186
+ if (!records.length) {
192
187
  const current = await this.getCurrentBranch(
193
188
  commonPath,
194
189
  metadataMap,
@@ -202,25 +197,28 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
202
197
 
203
198
  const branches: GitBranch[] = [];
204
199
 
205
- for (const entry of parser.parse(result.stdout)) {
200
+ for (const record of records) {
201
+ const fullName = record.name;
202
+ // Filter to branches and remote-tracking branches; skip tags from the unified records.
203
+ if (!fullName.startsWith('refs/heads/') && !fullName.startsWith('refs/remotes/')) continue;
206
204
  // Skip HEAD refs in remote branches
207
- if (isRemoteHEAD(entry.name)) continue;
205
+ if (isRemoteHEAD(fullName)) continue;
208
206
 
209
- const upstream = parseUpstream(entry.upstream, entry.upstreamTracking);
207
+ const upstream = parseUpstream(record.upstream, record.upstreamTracking);
210
208
 
211
- const metadata = metadataMap.get(parseRefName(entry.name).name);
212
- const worktreePath = entry.worktreePath ? normalizePath(entry.worktreePath) : undefined;
209
+ const metadata = metadataMap.get(parseRefName(fullName).name);
210
+ const worktreePath = record.worktreePath ? normalizePath(record.worktreePath) : undefined;
213
211
 
214
212
  branches.push(
215
213
  new GitBranch(
216
214
  commonPath,
217
- entry.name,
215
+ fullName,
218
216
  false, // Don't trust %(HEAD) for current as it's per-worktree -- we will set it later
219
- entry.date ? new Date(entry.date) : undefined,
217
+ record.committerDate ? new Date(record.committerDate) : undefined,
220
218
  metadata,
221
- entry.sha,
219
+ record.objectname,
222
220
  upstream,
223
- supported.includes('git:for-each-ref:worktreePath')
221
+ worktreePathSupported
224
222
  ? worktreePath
225
223
  ? { path: worktreePath, isDefault: worktreePath === defaultWorktreePath }
226
224
  : false
@@ -360,55 +358,42 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
360
358
  async getBranchContributionsOverview(
361
359
  repoPath: string,
362
360
  ref: string,
363
- options?: { associatedPullRequest?: Promise<{ refs?: { base?: { branch: string } } } | undefined> },
361
+ options?: {
362
+ associatedPullRequest?: Promise<{ refs?: { base?: { branch: string } } } | undefined>;
363
+ priority?: GitCommandPriority;
364
+ },
364
365
  cancellation?: AbortSignal,
365
366
  ): Promise<BranchContributionsOverview | undefined> {
366
367
  const scope = getScopedLogger();
368
+ const priority = options?.priority;
369
+ const priorityOpts = priority != null ? { priority: priority } : undefined;
370
+
371
+ // Expensive body — runs only on cache miss. Closes over `mergeTarget` so the cache key
372
+ // (`${ref}|${mergeTarget}`) fully captures every input that affects the result.
373
+ const fetchBody = async (
374
+ mergeTarget: string,
375
+ signal: AbortSignal | undefined,
376
+ ): Promise<BranchContributionsOverview | undefined> => {
377
+ const mergeBase = await this.provider.refs.getMergeBase(repoPath, ref, mergeTarget, priorityOpts, signal);
378
+ if (mergeBase == null) return undefined;
367
379
 
368
- try {
369
- const branch = await this.getBranch(repoPath, ref, cancellation);
370
- if (branch == null) return undefined;
371
-
372
- let mergeTarget: string | undefined;
373
-
374
- // Tier 1: Check stored merge target (user-set or previously detected from PR)
375
- const storedTarget = await this.getStoredMergeTargetBranchName(repoPath, ref);
376
- if (storedTarget) {
377
- const validated = await this.provider.refs.getSymbolicReferenceName(repoPath, storedTarget);
378
- mergeTarget = validated || storedTarget;
379
- }
380
-
381
- // Tier 2: PR-based lookup (caller provides the PR promise)
382
- if (mergeTarget == null && options?.associatedPullRequest != null) {
383
- const pr = await options.associatedPullRequest;
384
- if (pr?.refs?.base != null) {
385
- mergeTarget = `${branch.remoteName}/${pr.refs.base.branch}`;
386
- // Store for future reuse
387
- void this.storeMergeTargetBranchName(repoPath, ref, mergeTarget);
388
- }
389
- }
390
-
391
- // Tier 3: Base branch, then default branch
392
- mergeTarget ??= await this.getBaseBranchName(repoPath, ref, cancellation);
393
- mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined, cancellation);
394
-
395
- if (mergeTarget == null) return undefined;
380
+ // Fetch the merge-base commit's dates in parallel with contributors so consumers
381
+ // (e.g. the graph minimap scope window) don't pay a separate round-trip for them.
382
+ // `allSettled` so a transient failure in one side doesn't drop the overview entirely.
383
+ const [contributorsSettled, mergeBaseDatesSettled] = await Promise.allSettled([
384
+ this.provider.contributors.getContributors(
385
+ repoPath,
386
+ createRevisionRange(mergeBase, ref, '..'),
387
+ { stats: true },
388
+ signal,
389
+ ),
390
+ this.provider.commits.getCommitDates(repoPath, mergeBase, signal),
391
+ ]);
396
392
 
397
- const mergeBase = await this.provider.refs.getMergeBase(
398
- repoPath,
399
- ref,
400
- mergeTarget,
401
- undefined,
402
- cancellation,
403
- );
404
- if (mergeBase == null) return undefined;
393
+ if (signal?.aborted) throw new CancellationError();
405
394
 
406
- const result = await this.provider.contributors.getContributors(
407
- repoPath,
408
- createRevisionRange(mergeBase, ref, '..'),
409
- { stats: true },
410
- cancellation,
411
- );
395
+ const result = getSettledValue(contributorsSettled) ?? { contributors: [] };
396
+ const mergeBaseDates = getSettledValue(mergeBaseDatesSettled);
412
397
 
413
398
  sortContributors(result.contributors, { orderBy: 'score:desc' });
414
399
 
@@ -446,6 +431,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
446
431
  branch: ref,
447
432
  mergeTarget: mergeTarget,
448
433
  mergeBase: mergeBase,
434
+ mergeBaseDate: mergeBaseDates?.committerDate,
449
435
 
450
436
  commits: totalCommits,
451
437
  files: totalFiles,
@@ -457,6 +443,64 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
457
443
 
458
444
  contributors: result.contributors,
459
445
  };
446
+ };
447
+
448
+ try {
449
+ // Resolve `mergeTarget` through Tier 1 → Tier 2 → Tier 3. The pieces of each tier are
450
+ // already individually cached (`getStoredMergeTargetBranchName`, `getBaseBranchName`,
451
+ // `getDefaultBranchName`), so this resolution path is cheap on warm caches. Only the
452
+ // expensive body below (`getMergeBase` + `getContributors` w/ stats + `getCommitDates`)
453
+ // is wrapped by `cache.getBranchOverview`, keyed on the resolved `mergeTarget` so any
454
+ // caller that resolves to the same target shares; differing targets get independent
455
+ // slots, which is the correctness constraint that previously forced the Tier 2/3 paths
456
+ // to bypass the cache entirely.
457
+ let mergeTarget: string | undefined;
458
+
459
+ // Tier 1: Stored merge target (user-set or previously detected from PR).
460
+ const storedTarget = await this.getStoredMergeTargetBranchName(repoPath, ref);
461
+ if (storedTarget) {
462
+ const validated = await this.provider.refs.getSymbolicReferenceName(
463
+ repoPath,
464
+ storedTarget,
465
+ priorityOpts,
466
+ cancellation,
467
+ );
468
+ mergeTarget = validated || storedTarget;
469
+ }
470
+
471
+ // Tier 2: PR-based lookup. The branch object is only needed here (for `remoteName`),
472
+ // so lazy-load it on the cold path instead of paying for it on every call.
473
+ if (mergeTarget == null && options?.associatedPullRequest != null) {
474
+ const pr = await options.associatedPullRequest;
475
+ if (pr?.refs?.base != null) {
476
+ const branch = await this.getBranch(repoPath, ref, cancellation);
477
+ if (branch == null) return undefined;
478
+
479
+ mergeTarget = `${branch.remoteName}/${pr.refs.base.branch}`;
480
+ // Store for future reuse — turns the next call into a Tier 1 path. Skip the
481
+ // `branchOverviews` invalidation that the write would otherwise trigger: we're
482
+ // about to populate `${ref}|${mergeTarget}` with this exact target, so evicting
483
+ // it would just force the next caller to redo the body. (gk-merge-target writes
484
+ // do not affect `baseBranchName`, so no need to skip that target.)
485
+ void this.storeMergeTargetBranchName(repoPath, ref, mergeTarget, {
486
+ skipInvalidation: ['branchOverviews'],
487
+ });
488
+ }
489
+ }
490
+
491
+ // Tier 3: Base branch, then default branch.
492
+ mergeTarget ??= await this.getBaseBranchName(repoPath, ref, priorityOpts, cancellation);
493
+ mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined, priorityOpts, cancellation);
494
+
495
+ if (mergeTarget == null) return undefined;
496
+
497
+ const resolvedTarget = mergeTarget;
498
+ return await this.cache.getBranchOverview(
499
+ repoPath,
500
+ `${ref}|${resolvedTarget}`,
501
+ (_commonPath, _cacheable, signal) => fetchBody(resolvedTarget, signal ?? cancellation),
502
+ { cancellation: cancellation },
503
+ );
460
504
  } catch (ex) {
461
505
  scope?.error(ex);
462
506
  if (isCancellationError(ex)) throw ex;
@@ -520,7 +564,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
520
564
  params.push(options.name);
521
565
  }
522
566
 
523
- return this.git.exec(
567
+ return this.git.run(
524
568
  {
525
569
  cwd: repoPath,
526
570
  cancellation: cancellation,
@@ -563,7 +607,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
563
607
  let data: [string, string | undefined] | undefined;
564
608
  let result;
565
609
  try {
566
- result = await this.git.exec(
610
+ result = await this.git.run(
567
611
  { cwd: repoPath, cancellation: signal, errors: 'throw' },
568
612
  'rev-parse',
569
613
  '--abbrev-ref',
@@ -582,7 +626,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
582
626
  data = [ex.stdout, undefined];
583
627
  } else {
584
628
  try {
585
- result = await this.git.exec(
629
+ result = await this.git.run(
586
630
  { cwd: repoPath, cancellation: signal },
587
631
  'symbolic-ref',
588
632
  '--short',
@@ -596,7 +640,12 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
596
640
  }
597
641
 
598
642
  if (data == null) {
599
- const symbolicRef = await this.getDefaultBranchName(repoPath, 'origin', signal);
643
+ const symbolicRef = await this.getDefaultBranchName(
644
+ repoPath,
645
+ 'origin',
646
+ undefined,
647
+ signal,
648
+ );
600
649
  if (symbolicRef != null) {
601
650
  data = [
602
651
  symbolicRef.startsWith('origin/')
@@ -629,7 +678,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
629
678
  }
630
679
  }
631
680
  } else if (GitWarnings.headNotABranch.test(msg)) {
632
- result = await this.git.exec(
681
+ result = await this.git.run(
633
682
  {
634
683
  cwd: repoPath,
635
684
  cancellation: signal,
@@ -677,18 +726,21 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
677
726
  async getDefaultBranchName(
678
727
  repoPath: string | undefined,
679
728
  remote?: string,
729
+ options?: { priority?: GitCommandPriority },
680
730
  cancellation?: AbortSignal,
681
731
  ): Promise<string | undefined> {
682
732
  if (repoPath == null) return undefined;
683
733
 
684
734
  remote ??= 'origin';
735
+ const priority = options?.priority;
736
+ const priorityOpts = priority != null ? { priority: priority } : undefined;
685
737
 
686
738
  return this.cache.getDefaultBranchName(repoPath, remote, async commonPath => {
687
739
  let retried = false;
688
740
  while (true) {
689
741
  try {
690
- const result = await this.git.exec(
691
- { cwd: commonPath, cancellation: cancellation },
742
+ const result = await this.git.run(
743
+ { cwd: commonPath, cancellation: cancellation, ...priorityOpts },
692
744
  'symbolic-ref',
693
745
  '--short',
694
746
  `refs/remotes/${remote}/HEAD`,
@@ -699,8 +751,8 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
699
751
  try {
700
752
  if (!retried) {
701
753
  retried = true;
702
- await this.git.exec(
703
- { cwd: commonPath, cancellation: cancellation },
754
+ await this.git.run(
755
+ { cwd: commonPath, cancellation: cancellation, ...priorityOpts },
704
756
  'remote',
705
757
  'set-head',
706
758
  '-a',
@@ -709,8 +761,8 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
709
761
  continue;
710
762
  }
711
763
 
712
- const result = await this.git.exec(
713
- { cwd: commonPath, cancellation: cancellation },
764
+ const result = await this.git.run(
765
+ { cwd: commonPath, cancellation: cancellation, ...priorityOpts },
714
766
  'ls-remote',
715
767
  '--symref',
716
768
  remote,
@@ -741,7 +793,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
741
793
  args.push('--no-track');
742
794
  }
743
795
  try {
744
- await this.git.exec({ cwd: repoPath }, ...args);
796
+ await this.git.run({ cwd: repoPath }, ...args);
745
797
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
746
798
  this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
747
799
  } catch (ex) {
@@ -767,7 +819,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
767
819
  const branches = ensureArray(names);
768
820
  const args = ['branch', options?.force ? '-D' : '-d', ...branches];
769
821
  try {
770
- await this.git.exec({ cwd: repoPath }, ...args);
822
+ await this.git.run({ cwd: repoPath }, ...args);
771
823
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
772
824
  this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
773
825
  } catch (ex) {
@@ -795,7 +847,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
795
847
  const branches = ensureArray(names);
796
848
  const args = ['push', '-d', remote, ...branches];
797
849
  try {
798
- await this.git.exec({ cwd: repoPath }, ...args);
850
+ await this.git.run({ cwd: repoPath }, ...args);
799
851
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
800
852
  this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
801
853
  } catch (ex) {
@@ -815,42 +867,67 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
815
867
  }
816
868
 
817
869
  @debug()
818
- async getBranchMergedStatus(
870
+ getBranchMergedStatus(
819
871
  repoPath: string,
820
872
  branch: GitBranchReference,
821
873
  into: GitBranchReference,
822
874
  cancellation?: AbortSignal,
823
875
  ): Promise<GitBranchMergedStatus> {
824
876
  if (branch.name === into.name || branch.upstream?.name === into.name) {
825
- return { merged: false };
877
+ return Promise.resolve({ merged: false });
826
878
  }
827
879
 
828
- const result = await this.getBranchMergedStatusCore(repoPath, branch, into, cancellation);
829
- if (result.merged) return result;
880
+ // Cache key omits repoPath so worktrees of the same common repo share the cached answer.
881
+ // Branch refs (`refs/heads/*` / `refs/remotes/*`) are unique within a common repo, so
882
+ // `name` + `remote` flag is the minimal stable identity.
883
+ const cacheKey = `${branch.remote ? 'r' : 'l'}:${branch.name}|${into.remote ? 'r' : 'l'}:${into.name}`;
830
884
 
831
- // If the branch we are checking is a remote branch, check if it has been merged into its local branch (if there is one)
832
- if (into.remote) {
833
- const localIntoBranch = await this.getLocalBranchByUpstream(repoPath, into.name, cancellation);
834
- // If there is a local branch and it is not the branch we are checking, check if it has been merged into it
835
- if (localIntoBranch != null && localIntoBranch.name !== branch.name) {
836
- const result = await this.getBranchMergedStatusCore(repoPath, branch, localIntoBranch, cancellation);
837
- if (result.merged) {
838
- return {
839
- ...result,
840
- localBranchOnly: createReference(localIntoBranch.ref, localIntoBranch.repoPath, {
841
- id: localIntoBranch.id,
842
- refType: 'branch',
843
- name: localIntoBranch.name,
844
- remote: localIntoBranch.remote,
845
- upstream: localIntoBranch.upstream,
846
- sha: localIntoBranch.sha,
847
- }),
848
- };
885
+ return this.cache.getBranchMergedStatus(
886
+ repoPath,
887
+ cacheKey,
888
+ async (commonPath, _cacheable, signal) => {
889
+ const result = await this.getBranchMergedStatusCore(commonPath, branch, into, signal);
890
+ if (result.merged) return result;
891
+
892
+ // If the branch we are checking is a remote branch, check if it has been merged into its local branch (if there is one)
893
+ if (into.remote) {
894
+ const localIntoBranch = await this.getLocalBranchByUpstream(commonPath, into.name, signal);
895
+ // If there is a local branch and it is not the branch we are checking, check if it has been merged into it
896
+ if (localIntoBranch != null && localIntoBranch.name !== branch.name) {
897
+ // Skip the second full merge-check cycle when the local branch points at the same commit as the remote —
898
+ // the merge-base/cherry/diff/apply pipeline would produce the same answer we already got for `into` above
899
+ if (localIntoBranch.sha != null && localIntoBranch.sha === into.sha) {
900
+ return { merged: false };
901
+ }
902
+
903
+ const result = await this.getBranchMergedStatusCore(
904
+ commonPath,
905
+ branch,
906
+ localIntoBranch,
907
+ signal,
908
+ );
909
+ if (result.merged) {
910
+ // `localBranchOnly` is built against `commonPath` here; the cache mapper rewrites
911
+ // `repoPath`/`id` to the requesting worktree on retrieval.
912
+ return {
913
+ ...result,
914
+ localBranchOnly: createReference(localIntoBranch.ref, localIntoBranch.repoPath, {
915
+ id: localIntoBranch.id,
916
+ refType: 'branch',
917
+ name: localIntoBranch.name,
918
+ remote: localIntoBranch.remote,
919
+ upstream: localIntoBranch.upstream,
920
+ sha: localIntoBranch.sha,
921
+ }),
922
+ };
923
+ }
924
+ }
849
925
  }
850
- }
851
- }
852
926
 
853
- return { merged: false };
927
+ return { merged: false };
928
+ },
929
+ cancellation,
930
+ );
854
931
  }
855
932
 
856
933
  private async getBranchMergedStatusCore(
@@ -864,7 +941,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
864
941
  try {
865
942
  // Check if branch is direct ancestor (handles FF merges)
866
943
  try {
867
- await this.git.exec(
944
+ await this.git.run(
868
945
  { cwd: repoPath, cancellation: cancellation, errors: 'throw' },
869
946
  'merge-base',
870
947
  '--is-ancestor',
@@ -877,7 +954,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
877
954
  }
878
955
 
879
956
  // Cherry-pick detection (handles cherry-picks, rebases, etc)
880
- let result = await this.git.exec(
957
+ let result = await this.git.run(
881
958
  { cwd: repoPath, cancellation: cancellation },
882
959
  'cherry',
883
960
  '--abbrev',
@@ -904,7 +981,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
904
981
  undefined,
905
982
  cancellation,
906
983
  );
907
- result = await this.git.exec({ cwd: repoPath, cancellation: cancellation }, 'diff', mergeBase, branch.name);
984
+ result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'diff', mergeBase, branch.name);
908
985
  if (result.stdout) {
909
986
  // Create a temporary index file
910
987
  await using disposableIndex = await this.provider.staging.createTemporaryIndex(
@@ -914,7 +991,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
914
991
  );
915
992
  const { env } = disposableIndex;
916
993
 
917
- result = await this.git.exec(
994
+ result = await this.git.run(
918
995
  {
919
996
  cwd: repoPath,
920
997
  cancellation: cancellation,
@@ -977,7 +1054,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
977
1054
  const parentRefs = shas.map(c => `${c}^`);
978
1055
  let parentShas: string[];
979
1056
  try {
980
- const result = await this.git.exec(
1057
+ const result = await this.git.run(
981
1058
  { cwd: repoPath, cancellation: signal, errors: 'throw' },
982
1059
  'rev-parse',
983
1060
  ...parentRefs,
@@ -1022,7 +1099,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1022
1099
 
1023
1100
  let data;
1024
1101
  try {
1025
- const result = await this.git.exec(
1102
+ const result = await this.git.run(
1026
1103
  { cwd: repoPath, cancellation: signal, errors: 'throw' },
1027
1104
  'merge-tree',
1028
1105
  '-z',
@@ -1095,7 +1172,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1095
1172
  let currentTreeOid: string;
1096
1173
  try {
1097
1174
  // Get the initial target branch tree OID
1098
- const treeResult = await this.git.exec(
1175
+ const treeResult = await this.git.run(
1099
1176
  { cwd: repoPath, cancellation: cancellation, errors: 'throw' },
1100
1177
  'rev-parse',
1101
1178
  `${targetBranch}^{tree}`,
@@ -1103,6 +1180,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1103
1180
  currentTreeOid = treeResult.stdout.trim();
1104
1181
  } catch (ex) {
1105
1182
  if (isCancellationError(ex)) throw ex;
1183
+
1106
1184
  scope?.error(ex, `Failed to resolve target branch '${targetBranch}'`);
1107
1185
  return createConflictDetectionError('refNotFound');
1108
1186
  }
@@ -1121,7 +1199,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1121
1199
  // - base = commit.parent (where the commit started)
1122
1200
  // - ours = currentTreeOid (current state of the target)
1123
1201
  // - theirs = commit.sha (what we're cherry-picking)
1124
- const result = await this.git.exec(
1202
+ const result = await this.git.run(
1125
1203
  { cwd: repoPath, cancellation: cancellation, errors: 'throw' },
1126
1204
  'merge-tree',
1127
1205
  '--write-tree',
@@ -1210,47 +1288,82 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1210
1288
  }
1211
1289
 
1212
1290
  @debug({ exit: true })
1213
- async getBaseBranchName(repoPath: string, ref: string, cancellation?: AbortSignal): Promise<string | undefined> {
1214
- try {
1215
- // getGkConfig has built-in fallback to regular config for backward compatibility
1216
- let mergeBase = await this.provider.config.getGkConfig(repoPath, `branch.${ref}.gk-merge-base`);
1217
- let update = false;
1218
-
1219
- // Also check vscode-merge-base in regular config (VS Code compatibility)
1220
- if (mergeBase == null) {
1221
- mergeBase = await this.provider.config.getConfig(repoPath, `branch.${ref}.vscode-merge-base`);
1222
- update = mergeBase != null;
1223
- }
1291
+ getBaseBranchName(
1292
+ repoPath: string,
1293
+ ref: string,
1294
+ options?: { priority?: GitCommandPriority },
1295
+ cancellation?: AbortSignal,
1296
+ ): Promise<string | undefined> {
1297
+ const priority = options?.priority;
1298
+ const priorityOpts = priority != null ? { priority: priority } : undefined;
1224
1299
 
1225
- if (mergeBase != null) {
1226
- const branch = await this.provider.refs.getSymbolicReferenceName(repoPath, mergeBase);
1227
- if (branch != null) {
1228
- if (update) {
1229
- void this.storeBaseBranchName(repoPath, ref, branch);
1300
+ return this.cache.getBaseBranchName(
1301
+ repoPath,
1302
+ ref,
1303
+ async (commonPath, signal) => {
1304
+ try {
1305
+ // getGkConfig has built-in fallback to regular config for backward compatibility
1306
+ let mergeBase = await this.provider.config.getGkConfig(commonPath, `branch.${ref}.gk-merge-base`);
1307
+ let update = false;
1308
+
1309
+ // Also check vscode-merge-base in regular config (VS Code compatibility)
1310
+ if (mergeBase == null) {
1311
+ mergeBase = await this.provider.config.getConfig(commonPath, `branch.${ref}.vscode-merge-base`);
1312
+ update = mergeBase != null;
1230
1313
  }
1314
+
1315
+ if (mergeBase != null) {
1316
+ const branch = await this.provider.refs.getSymbolicReferenceName(
1317
+ commonPath,
1318
+ mergeBase,
1319
+ priorityOpts,
1320
+ );
1321
+ if (branch != null) {
1322
+ if (update) {
1323
+ // Self-write: the value we're storing is exactly what this factory is
1324
+ // returning, so any in-flight `branchOverviews` or `baseBranchName` entry
1325
+ // being populated against this resolution should NOT be evicted.
1326
+ void this.storeBaseBranchName(commonPath, ref, branch, {
1327
+ skipInvalidation: ['branchOverviews', 'baseBranchName'],
1328
+ });
1329
+ }
1330
+ return branch;
1331
+ }
1332
+ }
1333
+ } catch {}
1334
+
1335
+ const branch = await this.getBaseBranchFromReflog(
1336
+ commonPath,
1337
+ ref,
1338
+ { upstream: true, priority: priority },
1339
+ signal,
1340
+ );
1341
+ if (branch != null) {
1342
+ // Self-write: see comment on the migration path above.
1343
+ void this.storeBaseBranchName(commonPath, ref, branch, {
1344
+ skipInvalidation: ['branchOverviews', 'baseBranchName'],
1345
+ });
1231
1346
  return branch;
1232
1347
  }
1233
- }
1234
- } catch {}
1235
-
1236
- const branch = await this.getBaseBranchFromReflog(repoPath, ref, { upstream: true }, cancellation);
1237
- if (branch != null) {
1238
- void this.storeBaseBranchName(repoPath, ref, branch);
1239
- return branch;
1240
- }
1241
1348
 
1242
- return undefined;
1349
+ return undefined;
1350
+ },
1351
+ cancellation,
1352
+ );
1243
1353
  }
1244
1354
 
1245
1355
  private async getBaseBranchFromReflog(
1246
1356
  repoPath: string,
1247
1357
  ref: string,
1248
- options?: { upstream: true },
1358
+ options?: { upstream: true; priority?: GitCommandPriority },
1249
1359
  cancellation?: AbortSignal,
1250
1360
  ): Promise<string | undefined> {
1361
+ const priority = options?.priority;
1362
+ const priorityOpts = priority != null ? { priority: priority } : undefined;
1363
+
1251
1364
  try {
1252
- let result = await this.git.exec(
1253
- { cwd: repoPath, cancellation: cancellation },
1365
+ let result = await this.git.run(
1366
+ { cwd: repoPath, cancellation: cancellation, ...priorityOpts },
1254
1367
  'reflog',
1255
1368
  ref,
1256
1369
  '--grep-reflog=branch: Created from *.',
@@ -1265,18 +1378,22 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1265
1378
  let name: string | undefined = match[1];
1266
1379
  if (name !== 'HEAD') {
1267
1380
  if (options?.upstream) {
1268
- const upstream = await this.provider.refs.getSymbolicReferenceName(repoPath, `${name}@{u}`);
1381
+ const upstream = await this.provider.refs.getSymbolicReferenceName(
1382
+ repoPath,
1383
+ `${name}@{u}`,
1384
+ priorityOpts,
1385
+ );
1269
1386
  if (upstream) return upstream;
1270
1387
  }
1271
1388
 
1272
- name = await this.provider.refs.getSymbolicReferenceName(repoPath, name);
1389
+ name = await this.provider.refs.getSymbolicReferenceName(repoPath, name, priorityOpts);
1273
1390
  if (name) return name;
1274
1391
  }
1275
1392
  }
1276
1393
 
1277
1394
  // Check if branch was created from HEAD
1278
- result = await this.git.exec(
1279
- { cwd: repoPath, cancellation: cancellation },
1395
+ result = await this.git.run(
1396
+ { cwd: repoPath, cancellation: cancellation, ...priorityOpts },
1280
1397
  'reflog',
1281
1398
  'HEAD',
1282
1399
  `--grep-reflog=checkout: moving from .* to ${ref.replace('refs/heads/', '')}`,
@@ -1289,11 +1406,15 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1289
1406
  if (match?.length === 2) {
1290
1407
  let name: string | undefined = match[1];
1291
1408
  if (options?.upstream) {
1292
- const upstream = await this.provider.refs.getSymbolicReferenceName(repoPath, `${name}@{u}`);
1409
+ const upstream = await this.provider.refs.getSymbolicReferenceName(
1410
+ repoPath,
1411
+ `${name}@{u}`,
1412
+ priorityOpts,
1413
+ );
1293
1414
  if (upstream) return upstream;
1294
1415
  }
1295
1416
 
1296
- name = await this.provider.refs.getSymbolicReferenceName(repoPath, name);
1417
+ name = await this.provider.refs.getSymbolicReferenceName(repoPath, name, priorityOpts);
1297
1418
  if (name) return name;
1298
1419
  }
1299
1420
  } catch (ex) {
@@ -1347,11 +1468,21 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1347
1468
  await this.storeBranchMetadata(repoPath, 'gk-last-modified', branch.name, now);
1348
1469
  }
1349
1470
 
1471
+ /** Updates the agent last activity timestamp for the current branch */
1472
+ @debounce(2.5 * 60 * 1000)
1473
+ @debug()
1474
+ async onCurrentBranchAgentActivity(repoPath: string): Promise<void> {
1475
+ const branch = await this.getBranch(repoPath);
1476
+ if (branch == null || branch.remote || branch.detached) return;
1477
+
1478
+ await this.storeBranchMetadata(repoPath, 'gk-agent-last-activity', branch.name, new Date());
1479
+ }
1480
+
1350
1481
  @debug()
1351
1482
  async renameBranch(repoPath: string, oldName: string, newName: string): Promise<void> {
1352
1483
  const args = ['branch', '-m', oldName, newName];
1353
1484
  try {
1354
- await this.git.exec({ cwd: repoPath }, ...args);
1485
+ await this.git.run({ cwd: repoPath }, ...args);
1355
1486
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
1356
1487
  this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
1357
1488
  } catch (ex) {
@@ -1377,7 +1508,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1377
1508
  const args =
1378
1509
  upstream == null ? ['branch', '--unset-upstream', name] : ['branch', '--set-upstream-to', upstream, name];
1379
1510
  try {
1380
- await this.git.exec({ cwd: repoPath }, ...args);
1511
+ await this.git.run({ cwd: repoPath }, ...args);
1381
1512
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
1382
1513
  this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
1383
1514
  } catch (ex) {
@@ -1399,13 +1530,23 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1399
1530
  }
1400
1531
 
1401
1532
  @debug()
1402
- async storeBaseBranchName(repoPath: string, ref: string, base: string): Promise<void> {
1403
- await this.provider.config.setGkConfig(repoPath, `branch.${ref}.gk-merge-base`, base);
1533
+ async storeBaseBranchName(
1534
+ repoPath: string,
1535
+ ref: string,
1536
+ base: string,
1537
+ options?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },
1538
+ ): Promise<void> {
1539
+ await this.provider.config.setGkConfig(repoPath, `branch.${ref}.gk-merge-base`, base, options);
1404
1540
  }
1405
1541
 
1406
1542
  @debug()
1407
- async storeMergeTargetBranchName(repoPath: string, ref: string, target: string): Promise<void> {
1408
- await this.provider.config.setGkConfig(repoPath, `branch.${ref}.gk-merge-target`, target);
1543
+ async storeMergeTargetBranchName(
1544
+ repoPath: string,
1545
+ ref: string,
1546
+ target: string,
1547
+ options?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },
1548
+ ): Promise<void> {
1549
+ await this.provider.config.setGkConfig(repoPath, `branch.${ref}.gk-merge-target`, target, options);
1409
1550
  }
1410
1551
 
1411
1552
  @debug()
@@ -1417,56 +1558,60 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
1417
1558
  * Gets all branch metadata (dates and disposition) from git config in a single batch operation.
1418
1559
  * @returns A map of branch name to metadata.
1419
1560
  */
1420
- private async getBranchMetadataMap(repoPath: string): Promise<Map<string, BranchMetadata>> {
1421
- const scope = getScopedLogger();
1422
- const metadataMap = new Map<string, BranchMetadata>();
1561
+ private getBranchMetadataMap(repoPath: string): Promise<Map<string, BranchMetadata>> {
1562
+ return this.cache.getBranchMetadataMap(repoPath, async commonPath => {
1563
+ const scope = getScopedLogger();
1564
+ const metadataMap = new Map<string, BranchMetadata>();
1423
1565
 
1424
- try {
1425
- // Use git config --get-regexp to load all gk-* branch metadata in one call
1426
- const configMap = await this.provider.config.getGkConfigRegex(
1427
- repoPath,
1428
- '^branch\\..*\\.gk-(last-(accessed|modified)|disposition)$',
1429
- );
1430
- if (!configMap.size) return metadataMap;
1566
+ try {
1567
+ // Use git config --get-regexp to load all gk-* branch metadata in one call
1568
+ const configMap = await this.provider.config.getGkConfigRegex(
1569
+ commonPath,
1570
+ '^branch\\..*\\.gk-(last-(accessed|modified)|disposition|agent-last-activity)$',
1571
+ );
1572
+ if (!configMap.size) return metadataMap;
1431
1573
 
1432
- for (const [key, value] of configMap) {
1433
- // Extract branch name and metadata key from "branch.{name}.gk-{key}"
1434
- if (!key.startsWith('branch.')) continue;
1574
+ for (const [key, value] of configMap) {
1575
+ // Extract branch name and metadata key from "branch.{name}.gk-{key}"
1576
+ if (!key.startsWith('branch.')) continue;
1435
1577
 
1436
- const keyParts = key.split('.');
1437
- if (keyParts.length < 3) continue;
1578
+ const keyParts = key.split('.');
1579
+ if (keyParts.length < 3) continue;
1438
1580
 
1439
- // Branch name is everything between "branch." and the last ".gk-*"
1440
- const metaKey = keyParts.at(-1);
1441
- const branchName = keyParts.slice(1, -1).join('.');
1581
+ // Branch name is everything between "branch." and the last ".gk-*"
1582
+ const metaKey = keyParts.at(-1);
1583
+ const branchName = keyParts.slice(1, -1).join('.');
1442
1584
 
1443
- let metadata = metadataMap.get(branchName);
1444
- if (metadata == null) {
1445
- metadata = {};
1446
- metadataMap.set(branchName, metadata);
1447
- }
1585
+ let metadata = metadataMap.get(branchName);
1586
+ if (metadata == null) {
1587
+ metadata = {};
1588
+ metadataMap.set(branchName, metadata);
1589
+ }
1448
1590
 
1449
- if (metaKey === 'gk-last-accessed') {
1450
- metadata.lastAccessedAt = value;
1451
- } else if (metaKey === 'gk-last-modified') {
1452
- metadata.lastModifiedAt = value;
1453
- } else if (metaKey === 'gk-disposition') {
1454
- if (value === 'starred' || value === 'archived') {
1455
- metadata.disposition = value;
1591
+ if (metaKey === 'gk-last-accessed') {
1592
+ metadata.lastAccessedAt = value;
1593
+ } else if (metaKey === 'gk-last-modified') {
1594
+ metadata.lastModifiedAt = value;
1595
+ } else if (metaKey === 'gk-agent-last-activity') {
1596
+ metadata.agentLastActivityAt = value;
1597
+ } else if (metaKey === 'gk-disposition') {
1598
+ if (value === 'starred' || value === 'archived') {
1599
+ metadata.disposition = value;
1600
+ }
1456
1601
  }
1457
1602
  }
1603
+ } catch (ex) {
1604
+ debugger;
1605
+ scope?.error(ex);
1458
1606
  }
1459
- } catch (ex) {
1460
- debugger;
1461
- scope?.error(ex);
1462
- }
1463
1607
 
1464
- return metadataMap;
1608
+ return metadataMap;
1609
+ });
1465
1610
  }
1466
1611
 
1467
1612
  private async storeBranchMetadata(
1468
1613
  repoPath: string,
1469
- key: 'gk-last-accessed' | 'gk-last-modified',
1614
+ key: 'gk-last-accessed' | 'gk-last-modified' | 'gk-agent-last-activity',
1470
1615
  ref: string,
1471
1616
  date: Date,
1472
1617
  ): Promise<void> {