@gitkraken/core-gitlens 0.2.0 → 0.3.1

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 (448) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/dist/git/cache.d.ts +38 -8
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +120 -55
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/context.d.ts +19 -0
  7. package/dist/git/context.d.ts.map +1 -1
  8. package/dist/git/context.js.map +1 -1
  9. package/dist/git/features.d.ts +1 -1
  10. package/dist/git/features.d.ts.map +1 -1
  11. package/dist/git/features.js +1 -0
  12. package/dist/git/features.js.map +1 -1
  13. package/dist/git/models/commit.d.ts.map +1 -1
  14. package/dist/git/models/commit.js.map +1 -1
  15. package/dist/git/models/graph.d.ts +96 -0
  16. package/dist/git/models/graph.d.ts.map +1 -1
  17. package/dist/git/models/graph.js +24 -1
  18. package/dist/git/models/graph.js.map +1 -1
  19. package/dist/git/models/reference.d.ts +31 -0
  20. package/dist/git/models/reference.d.ts.map +1 -1
  21. package/dist/git/models/remote.d.ts.map +1 -1
  22. package/dist/git/models/remote.js.map +1 -1
  23. package/dist/git/models/remoteProvider.d.ts +1 -0
  24. package/dist/git/models/remoteProvider.d.ts.map +1 -1
  25. package/dist/git/models/remoteProvider.js.map +1 -1
  26. package/dist/git/models/repository.d.ts +30 -13
  27. package/dist/git/models/repository.d.ts.map +1 -1
  28. package/dist/git/models/repository.js +89 -87
  29. package/dist/git/models/repository.js.map +1 -1
  30. package/dist/git/models/search.d.ts +2 -2
  31. package/dist/git/models/search.d.ts.map +1 -1
  32. package/dist/git/models/statusFile.d.ts +9 -0
  33. package/dist/git/models/statusFile.d.ts.map +1 -1
  34. package/dist/git/models/statusFile.js +11 -0
  35. package/dist/git/models/statusFile.js.map +1 -1
  36. package/dist/git/models/worktree.d.ts +4 -1
  37. package/dist/git/models/worktree.d.ts.map +1 -1
  38. package/dist/git/models/worktree.js +2 -2
  39. package/dist/git/models/worktree.js.map +1 -1
  40. package/dist/git/parsers/diffParser.d.ts.map +1 -1
  41. package/dist/git/parsers/diffParser.js.map +1 -1
  42. package/dist/git/providers/branches.d.ts +15 -4
  43. package/dist/git/providers/branches.d.ts.map +1 -1
  44. package/dist/git/providers/commits.d.ts +4 -0
  45. package/dist/git/providers/commits.d.ts.map +1 -1
  46. package/dist/git/providers/config.d.ts +5 -4
  47. package/dist/git/providers/config.d.ts.map +1 -1
  48. package/dist/git/providers/operations.d.ts +61 -12
  49. package/dist/git/providers/operations.d.ts.map +1 -1
  50. package/dist/git/providers/pausedOperations.d.ts +3 -1
  51. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  52. package/dist/git/providers/refs.d.ts +23 -2
  53. package/dist/git/providers/refs.d.ts.map +1 -1
  54. package/dist/git/providers/staging.d.ts +16 -0
  55. package/dist/git/providers/staging.d.ts.map +1 -1
  56. package/dist/git/providers/status.d.ts +4 -1
  57. package/dist/git/providers/status.d.ts.map +1 -1
  58. package/dist/git/remotes/azure-devops.d.ts.map +1 -1
  59. package/dist/git/remotes/azure-devops.js.map +1 -1
  60. package/dist/git/remotes/custom.d.ts +2 -0
  61. package/dist/git/remotes/custom.d.ts.map +1 -1
  62. package/dist/git/remotes/custom.js +20 -0
  63. package/dist/git/remotes/custom.js.map +1 -1
  64. package/dist/git/{exec.types.d.ts → run.types.d.ts} +22 -3
  65. package/dist/git/run.types.d.ts.map +1 -0
  66. package/dist/git/run.types.js +2 -0
  67. package/dist/git/run.types.js.map +1 -0
  68. package/dist/git/service.d.ts.map +1 -1
  69. package/dist/git/service.js.map +1 -1
  70. package/dist/git/utils/autolink.utils.d.ts.map +1 -1
  71. package/dist/git/utils/autolink.utils.js.map +1 -1
  72. package/dist/git/utils/blame.utils.d.ts.map +1 -1
  73. package/dist/git/utils/blame.utils.js.map +1 -1
  74. package/dist/git/utils/conflictResolution.utils.d.ts +6 -0
  75. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -0
  76. package/dist/git/utils/conflictResolution.utils.js +25 -0
  77. package/dist/git/utils/conflictResolution.utils.js.map +1 -0
  78. package/dist/git/utils/contributor.utils.d.ts +3 -0
  79. package/dist/git/utils/contributor.utils.d.ts.map +1 -1
  80. package/dist/git/utils/contributor.utils.js +22 -0
  81. package/dist/git/utils/contributor.utils.js.map +1 -1
  82. package/dist/git/utils/fileStatus.utils.d.ts +2 -1
  83. package/dist/git/utils/fileStatus.utils.d.ts.map +1 -1
  84. package/dist/git/utils/fileStatus.utils.js +4 -0
  85. package/dist/git/utils/fileStatus.utils.js.map +1 -1
  86. package/dist/git/utils/reachability.utils.d.ts +37 -0
  87. package/dist/git/utils/reachability.utils.d.ts.map +1 -0
  88. package/dist/git/utils/reachability.utils.js +96 -0
  89. package/dist/git/utils/reachability.utils.js.map +1 -0
  90. package/dist/git/utils/search.utils.d.ts.map +1 -1
  91. package/dist/git/utils/search.utils.js +3 -0
  92. package/dist/git/utils/search.utils.js.map +1 -1
  93. package/dist/git/utils/sorting.d.ts +8 -0
  94. package/dist/git/utils/sorting.d.ts.map +1 -1
  95. package/dist/git/utils/sorting.js +15 -0
  96. package/dist/git/utils/sorting.js.map +1 -1
  97. package/dist/git/utils/status.utils.d.ts.map +1 -1
  98. package/dist/git/utils/status.utils.js.map +1 -1
  99. package/dist/git/watching/classifyChange.d.ts.map +1 -1
  100. package/dist/git/watching/classifyChange.js +8 -0
  101. package/dist/git/watching/classifyChange.js.map +1 -1
  102. package/dist/git/watching/gitIgnoreFilter.js +1 -1
  103. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  104. package/dist/git/watching/initWatcher.d.ts.map +1 -1
  105. package/dist/git/watching/initWatcher.js.map +1 -1
  106. package/dist/git/watching/watchGroup.d.ts.map +1 -1
  107. package/dist/git/watching/watchGroup.js.map +1 -1
  108. package/dist/git/watching/watchService.d.ts +4 -2
  109. package/dist/git/watching/watchService.d.ts.map +1 -1
  110. package/dist/git/watching/watchService.js +31 -6
  111. package/dist/git/watching/watchService.js.map +1 -1
  112. package/dist/git/watching/watchSession.d.ts +14 -12
  113. package/dist/git/watching/watchSession.d.ts.map +1 -1
  114. package/dist/git/watching/watchSession.js +56 -55
  115. package/dist/git/watching/watchSession.js.map +1 -1
  116. package/dist/git-cli/exec/exec.types.d.ts +1 -1
  117. package/dist/git-cli/exec/exec.types.d.ts.map +1 -1
  118. package/dist/git-cli/exec/git.d.ts +3 -3
  119. package/dist/git-cli/exec/git.d.ts.map +1 -1
  120. package/dist/git-cli/exec/git.js.map +1 -1
  121. package/dist/git-cli/exec/gitQueue.d.ts +10 -2
  122. package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
  123. package/dist/git-cli/exec/gitQueue.js +39 -12
  124. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  125. package/dist/git-cli/parsers/blameParser.d.ts.map +1 -1
  126. package/dist/git-cli/parsers/blameParser.js.map +1 -1
  127. package/dist/git-cli/parsers/logParser.js.map +1 -1
  128. package/dist/git-cli/parsers/refParser.d.ts +3 -30
  129. package/dist/git-cli/parsers/refParser.d.ts.map +1 -1
  130. package/dist/git-cli/parsers/refParser.js +30 -30
  131. package/dist/git-cli/parsers/refParser.js.map +1 -1
  132. package/dist/git-cli/providers/blame.d.ts.map +1 -1
  133. package/dist/git-cli/providers/blame.js.map +1 -1
  134. package/dist/git-cli/providers/branches.d.ts +15 -5
  135. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  136. package/dist/git-cli/providers/branches.js +99 -68
  137. package/dist/git-cli/providers/branches.js.map +1 -1
  138. package/dist/git-cli/providers/commits.d.ts +1 -0
  139. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  140. package/dist/git-cli/providers/commits.js +86 -30
  141. package/dist/git-cli/providers/commits.js.map +1 -1
  142. package/dist/git-cli/providers/config.d.ts +19 -6
  143. package/dist/git-cli/providers/config.d.ts.map +1 -1
  144. package/dist/git-cli/providers/config.js +86 -38
  145. package/dist/git-cli/providers/config.js.map +1 -1
  146. package/dist/git-cli/providers/diff.d.ts.map +1 -1
  147. package/dist/git-cli/providers/diff.js +5 -3
  148. package/dist/git-cli/providers/diff.js.map +1 -1
  149. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  150. package/dist/git-cli/providers/graph.js +139 -16
  151. package/dist/git-cli/providers/graph.js.map +1 -1
  152. package/dist/git-cli/providers/operations.d.ts +17 -14
  153. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  154. package/dist/git-cli/providers/operations.js +135 -63
  155. package/dist/git-cli/providers/operations.js.map +1 -1
  156. package/dist/git-cli/providers/patch.d.ts.map +1 -1
  157. package/dist/git-cli/providers/patch.js.map +1 -1
  158. package/dist/git-cli/providers/pausedOperations.d.ts +3 -1
  159. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  160. package/dist/git-cli/providers/pausedOperations.js +7 -1
  161. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  162. package/dist/git-cli/providers/refs.d.ts +19 -2
  163. package/dist/git-cli/providers/refs.d.ts.map +1 -1
  164. package/dist/git-cli/providers/refs.js +286 -2
  165. package/dist/git-cli/providers/refs.js.map +1 -1
  166. package/dist/git-cli/providers/revision.js.map +1 -1
  167. package/dist/git-cli/providers/staging.d.ts +6 -0
  168. package/dist/git-cli/providers/staging.d.ts.map +1 -1
  169. package/dist/git-cli/providers/staging.js +32 -0
  170. package/dist/git-cli/providers/staging.js.map +1 -1
  171. package/dist/git-cli/providers/stash.d.ts.map +1 -1
  172. package/dist/git-cli/providers/stash.js +20 -2
  173. package/dist/git-cli/providers/stash.js.map +1 -1
  174. package/dist/git-cli/providers/status.d.ts +4 -1
  175. package/dist/git-cli/providers/status.d.ts.map +1 -1
  176. package/dist/git-cli/providers/status.js +8 -4
  177. package/dist/git-cli/providers/status.js.map +1 -1
  178. package/dist/git-cli/providers/tags.d.ts.map +1 -1
  179. package/dist/git-cli/providers/tags.js +10 -6
  180. package/dist/git-cli/providers/tags.js.map +1 -1
  181. package/dist/ipc/discovery.d.ts +49 -0
  182. package/dist/ipc/discovery.d.ts.map +1 -0
  183. package/dist/ipc/discovery.js +147 -0
  184. package/dist/ipc/discovery.js.map +1 -0
  185. package/dist/ipc/ipcServer.d.ts +20 -0
  186. package/dist/ipc/ipcServer.d.ts.map +1 -0
  187. package/dist/ipc/ipcServer.js +162 -0
  188. package/dist/ipc/ipcServer.js.map +1 -0
  189. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +167 -0
  190. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -0
  191. package/dist/plus/agents/providers/claudeCodeProvider.js +1786 -0
  192. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -0
  193. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +36 -0
  194. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -0
  195. package/dist/plus/agents/providers/claudeCodeTranscript.js +171 -0
  196. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -0
  197. package/dist/plus/agents/sanitizePrompt.d.ts +15 -0
  198. package/dist/plus/agents/sanitizePrompt.d.ts.map +1 -0
  199. package/dist/plus/agents/sanitizePrompt.js +103 -0
  200. package/dist/plus/agents/sanitizePrompt.js.map +1 -0
  201. package/dist/plus/agents/stateMachine.d.ts +32 -0
  202. package/dist/plus/agents/stateMachine.d.ts.map +1 -0
  203. package/dist/plus/agents/stateMachine.js +180 -0
  204. package/dist/plus/agents/stateMachine.js.map +1 -0
  205. package/dist/plus/agents/types.d.ts +254 -0
  206. package/dist/plus/agents/types.d.ts.map +1 -0
  207. package/dist/plus/agents/types.js +49 -0
  208. package/dist/plus/agents/types.js.map +1 -0
  209. package/dist/plus/ai/errors.d.ts +4 -1
  210. package/dist/plus/ai/errors.d.ts.map +1 -1
  211. package/dist/plus/ai/errors.js +38 -0
  212. package/dist/plus/ai/errors.js.map +1 -1
  213. package/dist/plus/ai/models/model.d.ts +1 -1
  214. package/dist/plus/ai/models/model.d.ts.map +1 -1
  215. package/dist/plus/ai/models/model.js.map +1 -1
  216. package/dist/plus/ai/models/promptTemplates.d.ts +11 -2
  217. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  218. package/dist/plus/ai/models/provider.d.ts +3 -0
  219. package/dist/plus/ai/models/provider.d.ts.map +1 -1
  220. package/dist/plus/ai/prompts.d.ts +1 -0
  221. package/dist/plus/ai/prompts.d.ts.map +1 -1
  222. package/dist/plus/ai/prompts.js +48 -8
  223. package/dist/plus/ai/prompts.js.map +1 -1
  224. package/dist/plus/ai/providers/gitkrakenProvider.d.ts +1 -1
  225. package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +1 -1
  226. package/dist/plus/ai/providers/gitkrakenProvider.js +4 -1
  227. package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
  228. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +4 -3
  229. package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +1 -1
  230. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +9 -6
  231. package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
  232. package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
  233. package/dist/plus/ai/utils/ai.utils.js +2 -0
  234. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  235. package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
  236. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  237. package/dist/plus/git-github/api/github.d.ts +12 -1
  238. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  239. package/dist/plus/git-github/api/github.js +245 -51
  240. package/dist/plus/git-github/api/github.js.map +1 -1
  241. package/dist/plus/git-github/api/types.d.ts +8 -0
  242. package/dist/plus/git-github/api/types.d.ts.map +1 -1
  243. package/dist/plus/git-github/models.d.ts +1 -1
  244. package/dist/plus/git-github/models.d.ts.map +1 -1
  245. package/dist/plus/git-github/models.js.map +1 -1
  246. package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
  247. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  248. package/dist/plus/git-github/providers/github/branches.d.ts +5 -1
  249. package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
  250. package/dist/plus/git-github/providers/github/branches.js +47 -24
  251. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  252. package/dist/plus/git-github/providers/github/commits.d.ts.map +1 -1
  253. package/dist/plus/git-github/providers/github/commits.js +144 -92
  254. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  255. package/dist/plus/git-github/providers/github/diff.d.ts +1 -1
  256. package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
  257. package/dist/plus/git-github/providers/github/diff.js +23 -9
  258. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  259. package/dist/plus/git-github/providers/github/refs.d.ts +9 -1
  260. package/dist/plus/git-github/providers/github/refs.d.ts.map +1 -1
  261. package/dist/plus/git-github/providers/github/refs.js +39 -14
  262. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  263. package/dist/plus/git-github/providers/github/revision.d.ts.map +1 -1
  264. package/dist/plus/git-github/providers/github/revision.js +1 -2
  265. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  266. package/dist/plus/git-github/providers/github/status.d.ts +4 -1
  267. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  268. package/dist/plus/git-github/providers/github/status.js +1 -1
  269. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  270. package/dist/plus/git-github/providers/github/tags.d.ts.map +1 -1
  271. package/dist/plus/git-github/providers/github/tags.js +35 -36
  272. package/dist/plus/git-github/providers/github/tags.js.map +1 -1
  273. package/dist/plus/git-github/providers/githubGitProvider.d.ts.map +1 -1
  274. package/dist/plus/git-github/providers/githubGitProvider.js.map +1 -1
  275. package/dist/utils/color.d.ts.map +1 -1
  276. package/dist/utils/color.js.map +1 -1
  277. package/dist/utils/date.d.ts.map +1 -1
  278. package/dist/utils/date.js +6 -0
  279. package/dist/utils/date.js.map +1 -1
  280. package/dist/utils/debounce.js +1 -1
  281. package/dist/utils/decorators/serializable.d.ts.map +1 -1
  282. package/dist/utils/decorators/serializable.js.map +1 -1
  283. package/dist/utils/dedupedAsyncCache.d.ts +37 -0
  284. package/dist/utils/dedupedAsyncCache.d.ts.map +1 -0
  285. package/dist/utils/dedupedAsyncCache.js +68 -0
  286. package/dist/utils/dedupedAsyncCache.js.map +1 -0
  287. package/dist/utils/diff.d.ts +2 -0
  288. package/dist/utils/diff.d.ts.map +1 -0
  289. package/dist/utils/diff.js +53 -0
  290. package/dist/utils/diff.js.map +1 -0
  291. package/dist/utils/disposable.d.ts.map +1 -1
  292. package/dist/utils/disposable.js.map +1 -1
  293. package/dist/utils/env/node/exec.d.ts.map +1 -1
  294. package/dist/utils/env/node/exec.js.map +1 -1
  295. package/dist/utils/error.d.ts.map +1 -1
  296. package/dist/utils/error.js.map +1 -1
  297. package/dist/utils/iterable.d.ts +7 -1
  298. package/dist/utils/iterable.d.ts.map +1 -1
  299. package/dist/utils/iterable.js +12 -4
  300. package/dist/utils/iterable.js.map +1 -1
  301. package/dist/utils/logger.d.ts.map +1 -1
  302. package/dist/utils/logger.js.map +1 -1
  303. package/dist/utils/lruMap.d.ts +9 -0
  304. package/dist/utils/lruMap.d.ts.map +1 -1
  305. package/dist/utils/lruMap.js +26 -4
  306. package/dist/utils/lruMap.js.map +1 -1
  307. package/dist/utils/markdown.d.ts.map +1 -1
  308. package/dist/utils/markdown.js.map +1 -1
  309. package/dist/utils/mru.d.ts.map +1 -1
  310. package/dist/utils/mru.js.map +1 -1
  311. package/dist/utils/object.d.ts +22 -1
  312. package/dist/utils/object.d.ts.map +1 -1
  313. package/dist/utils/object.js +56 -3
  314. package/dist/utils/object.js.map +1 -1
  315. package/dist/utils/paging.js +14 -15
  316. package/dist/utils/paging.js.map +1 -1
  317. package/dist/utils/path.d.ts.map +1 -1
  318. package/dist/utils/path.js.map +1 -1
  319. package/dist/utils/promise.d.ts +1 -0
  320. package/dist/utils/promise.d.ts.map +1 -1
  321. package/dist/utils/promise.js +17 -0
  322. package/dist/utils/promise.js.map +1 -1
  323. package/dist/utils/promiseCache.d.ts +10 -0
  324. package/dist/utils/promiseCache.d.ts.map +1 -1
  325. package/dist/utils/promiseCache.js +34 -4
  326. package/dist/utils/promiseCache.js.map +1 -1
  327. package/dist/utils/string.d.ts.map +1 -1
  328. package/dist/utils/string.js +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.map +1 -1
  333. package/dist/utils/version.js.map +1 -1
  334. package/package.json +13 -1
  335. package/src/git/cache.ts +192 -90
  336. package/src/git/context.ts +21 -0
  337. package/src/git/features.ts +2 -0
  338. package/src/git/models/commit.ts +1 -0
  339. package/src/git/models/graph.ts +97 -0
  340. package/src/git/models/reference.ts +33 -0
  341. package/src/git/models/remote.ts +1 -0
  342. package/src/git/models/remoteProvider.ts +1 -0
  343. package/src/git/models/repository.ts +105 -96
  344. package/src/git/models/search.ts +2 -2
  345. package/src/git/models/statusFile.ts +12 -0
  346. package/src/git/models/worktree.ts +10 -2
  347. package/src/git/parsers/diffParser.ts +2 -0
  348. package/src/git/providers/branches.ts +22 -4
  349. package/src/git/providers/commits.ts +4 -0
  350. package/src/git/providers/config.ts +3 -5
  351. package/src/git/providers/operations.ts +65 -1
  352. package/src/git/providers/pausedOperations.ts +1 -0
  353. package/src/git/providers/refs.ts +32 -3
  354. package/src/git/providers/staging.ts +12 -0
  355. package/src/git/providers/status.ts +6 -1
  356. package/src/git/remotes/azure-devops.ts +2 -0
  357. package/src/git/remotes/custom.ts +23 -0
  358. package/src/git/{exec.types.ts → run.types.ts} +22 -2
  359. package/src/git/service.ts +1 -0
  360. package/src/git/utils/autolink.utils.ts +1 -0
  361. package/src/git/utils/blame.utils.ts +2 -0
  362. package/src/git/utils/conflictResolution.utils.ts +31 -0
  363. package/src/git/utils/contributor.utils.ts +24 -0
  364. package/src/git/utils/fileStatus.utils.ts +7 -1
  365. package/src/git/utils/reachability.utils.ts +109 -0
  366. package/src/git/utils/search.utils.ts +2 -0
  367. package/src/git/utils/sorting.ts +17 -0
  368. package/src/git/utils/status.utils.ts +1 -0
  369. package/src/git/watching/classifyChange.ts +9 -0
  370. package/src/git/watching/gitIgnoreFilter.ts +2 -2
  371. package/src/git/watching/initWatcher.ts +2 -0
  372. package/src/git/watching/watchGroup.ts +3 -0
  373. package/src/git/watching/watchService.ts +45 -7
  374. package/src/git/watching/watchSession.ts +71 -55
  375. package/src/git-cli/exec/exec.types.ts +2 -2
  376. package/src/git-cli/exec/git.ts +6 -5
  377. package/src/git-cli/exec/gitQueue.ts +41 -12
  378. package/src/git-cli/parsers/blameParser.ts +1 -0
  379. package/src/git-cli/parsers/logParser.ts +1 -0
  380. package/src/git-cli/parsers/refParser.ts +33 -34
  381. package/src/git-cli/providers/blame.ts +1 -0
  382. package/src/git-cli/providers/branches.ts +165 -93
  383. package/src/git-cli/providers/commits.ts +107 -37
  384. package/src/git-cli/providers/config.ts +101 -48
  385. package/src/git-cli/providers/diff.ts +10 -3
  386. package/src/git-cli/providers/graph.ts +156 -16
  387. package/src/git-cli/providers/operations.ts +208 -83
  388. package/src/git-cli/providers/patch.ts +1 -0
  389. package/src/git-cli/providers/pausedOperations.ts +8 -0
  390. package/src/git-cli/providers/refs.ts +247 -3
  391. package/src/git-cli/providers/revision.ts +2 -2
  392. package/src/git-cli/providers/staging.ts +33 -0
  393. package/src/git-cli/providers/stash.ts +20 -2
  394. package/src/git-cli/providers/status.ts +24 -5
  395. package/src/git-cli/providers/tags.ts +15 -16
  396. package/src/ipc/discovery.ts +186 -0
  397. package/src/ipc/ipcServer.ts +171 -0
  398. package/src/plus/agents/providers/claudeCodeProvider.ts +2122 -0
  399. package/src/plus/agents/providers/claudeCodeTranscript.ts +214 -0
  400. package/src/plus/agents/sanitizePrompt.ts +109 -0
  401. package/src/plus/agents/stateMachine.ts +197 -0
  402. package/src/plus/agents/types.ts +325 -0
  403. package/src/plus/ai/errors.ts +40 -0
  404. package/src/plus/ai/models/model.ts +1 -0
  405. package/src/plus/ai/models/promptTemplates.ts +13 -0
  406. package/src/plus/ai/models/provider.ts +7 -1
  407. package/src/plus/ai/prompts.ts +49 -8
  408. package/src/plus/ai/providers/gitkrakenProvider.ts +4 -0
  409. package/src/plus/ai/providers/openAICompatibleProviderBase.ts +21 -4
  410. package/src/plus/ai/utils/ai.utils.ts +2 -0
  411. package/src/plus/ai/utils/results.utils.ts +2 -0
  412. package/src/plus/git-github/api/github.ts +315 -57
  413. package/src/plus/git-github/api/types.ts +15 -0
  414. package/src/plus/git-github/models.ts +1 -1
  415. package/src/plus/git-github/providers/github/blame.ts +2 -0
  416. package/src/plus/git-github/providers/github/branches.ts +61 -31
  417. package/src/plus/git-github/providers/github/commits.ts +231 -160
  418. package/src/plus/git-github/providers/github/diff.ts +27 -19
  419. package/src/plus/git-github/providers/github/refs.ts +58 -23
  420. package/src/plus/git-github/providers/github/revision.ts +1 -2
  421. package/src/plus/git-github/providers/github/status.ts +6 -1
  422. package/src/plus/git-github/providers/github/tags.ts +61 -62
  423. package/src/plus/git-github/providers/githubGitProvider.ts +1 -0
  424. package/src/utils/color.ts +2 -0
  425. package/src/utils/date.ts +5 -0
  426. package/src/utils/debounce.ts +1 -1
  427. package/src/utils/decorators/serializable.ts +1 -0
  428. package/src/utils/dedupedAsyncCache.ts +74 -0
  429. package/src/utils/diff.ts +61 -0
  430. package/src/utils/disposable.ts +2 -0
  431. package/src/utils/env/node/exec.ts +2 -0
  432. package/src/utils/error.ts +1 -0
  433. package/src/utils/iterable.ts +17 -3
  434. package/src/utils/logger.ts +1 -0
  435. package/src/utils/lruMap.ts +29 -4
  436. package/src/utils/markdown.ts +1 -0
  437. package/src/utils/mru.ts +2 -0
  438. package/src/utils/object.ts +55 -4
  439. package/src/utils/paging.ts +14 -14
  440. package/src/utils/path.ts +1 -0
  441. package/src/utils/promise.ts +22 -0
  442. package/src/utils/promiseCache.ts +48 -16
  443. package/src/utils/string.ts +3 -1
  444. package/src/utils/uri.ts +1 -0
  445. package/src/utils/version.ts +1 -0
  446. package/dist/git/exec.types.d.ts.map +0 -1
  447. package/dist/git/exec.types.js +0 -2
  448. package/dist/git/exec.types.js.map +0 -1
@@ -0,0 +1,214 @@
1
+ import { createReadStream } from 'fs';
2
+ import { readdir, stat } from 'fs/promises';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+
6
+ export interface TranscriptTitles {
7
+ custom?: string;
8
+ ai?: string;
9
+ agent?: string;
10
+ }
11
+
12
+ interface CacheEntry {
13
+ path: string;
14
+ mtimeMs: number;
15
+ size: number;
16
+ nextOffset: number;
17
+ titles: TranscriptTitles;
18
+ }
19
+
20
+ interface TitleEntry {
21
+ type: string;
22
+ sessionId?: string;
23
+ customTitle?: string;
24
+ aiTitle?: string;
25
+ agentName?: string;
26
+ }
27
+
28
+ /**
29
+ * Reads Claude Code transcript JSONL files at `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`
30
+ * to surface `custom-title`, `ai-title`, and `agent-name` entries as fallback session names.
31
+ *
32
+ * Maintains a per-session cache keyed by mtime + last-read byte offset, so repeated calls only
33
+ * read the appended tail. Last occurrence per type wins.
34
+ *
35
+ * `resolve` is async, so a `forget` (or a newer `resolve`) for the same session can land between
36
+ * its `await`s. A per-session generation counter is stamped at entry and re-checked before the
37
+ * final cache write, so a stale `resolve` can't resurrect a forgotten entry or clobber a newer
38
+ * resolve's result.
39
+ */
40
+ export class ClaudeCodeTranscriptReader {
41
+ private readonly _cache = new Map<string, CacheEntry>();
42
+ private readonly _generations = new Map<string, number>();
43
+ private _nextGen = 0;
44
+
45
+ async resolve(sessionId: string, cwd: string | undefined): Promise<TranscriptTitles | undefined> {
46
+ const gen = ++this._nextGen;
47
+ this._generations.set(sessionId, gen);
48
+
49
+ const cached = this._cache.get(sessionId);
50
+ const path = cached?.path ?? (await this.locateTranscript(sessionId, cwd));
51
+ if (path == null) return undefined;
52
+
53
+ let stats;
54
+ try {
55
+ stats = await stat(path);
56
+ } catch {
57
+ // File disappeared; drop any stale cache entry so a future call retries discovery.
58
+ // Skip the delete if a newer resolve/forget claimed the slot — their state is fresher.
59
+ if (this._generations.get(sessionId) === gen) {
60
+ this._cache.delete(sessionId);
61
+ this._generations.delete(sessionId);
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ if (stats.mtimeMs === cached?.mtimeMs && stats.size === cached?.size) {
67
+ return cached.titles;
68
+ }
69
+
70
+ // Truncation or rewrite: stat shrank below our read cursor or mtime moved backward → full re-scan.
71
+ const startFromScratch = cached == null || stats.size < cached.nextOffset || stats.mtimeMs < cached.mtimeMs;
72
+ const startOffset = startFromScratch ? 0 : cached.nextOffset;
73
+ const baseTitles: TranscriptTitles = startFromScratch ? {} : { ...cached.titles };
74
+
75
+ const { titles, consumedEnd } =
76
+ stats.size > startOffset
77
+ ? await this.readRange(path, startOffset, stats.size, sessionId, baseTitles)
78
+ : { titles: baseTitles, consumedEnd: startOffset };
79
+
80
+ // If `forget` ran or a newer `resolve` started while we were awaiting, skip the cache write
81
+ // — the entry we'd be writing reflects stale state.
82
+ if (this._generations.get(sessionId) !== gen) return titles;
83
+
84
+ const entry: CacheEntry = {
85
+ path: path,
86
+ mtimeMs: stats.mtimeMs,
87
+ size: stats.size,
88
+ nextOffset: consumedEnd,
89
+ titles: titles,
90
+ };
91
+ this._cache.set(sessionId, entry);
92
+ return titles;
93
+ }
94
+
95
+ forget(sessionId: string): void {
96
+ this._cache.delete(sessionId);
97
+ this._generations.delete(sessionId);
98
+ }
99
+
100
+ protected async locateTranscript(sessionId: string, cwd: string | undefined): Promise<string | undefined> {
101
+ const root = join(homedir(), '.claude', 'projects');
102
+ const fileName = `${sessionId}.jsonl`;
103
+
104
+ if (cwd != null && cwd.length > 0) {
105
+ const encoded = cwd.replace(/[/\\:]/g, '-');
106
+ const candidate = join(root, encoded, fileName);
107
+ if (await fileExists(candidate)) return candidate;
108
+ }
109
+
110
+ let dirs: string[];
111
+ try {
112
+ dirs = await readdir(root);
113
+ } catch {
114
+ return undefined;
115
+ }
116
+
117
+ const candidates = await Promise.all(
118
+ dirs.map(async dir => {
119
+ const candidate = join(root, dir, fileName);
120
+ return (await fileExists(candidate)) ? candidate : undefined;
121
+ }),
122
+ );
123
+ return candidates.find(c => c != null);
124
+ }
125
+
126
+ /**
127
+ * Reads bytes `[start, end)` from `path` and applies any title entries it finds to a copy of
128
+ * `baseTitles`. Returns the merged titles and the byte position immediately after the last
129
+ * newline observed — partial trailing lines (writer hasn't flushed `\n` yet) are intentionally
130
+ * left unconsumed so they're re-read on the next pass.
131
+ */
132
+ protected async readRange(
133
+ path: string,
134
+ start: number,
135
+ end: number,
136
+ sessionId: string,
137
+ baseTitles: TranscriptTitles,
138
+ ): Promise<{ titles: TranscriptTitles; consumedEnd: number }> {
139
+ const buffer = await readSlice(path, start, end);
140
+ const titles: TranscriptTitles = { ...baseTitles };
141
+
142
+ let lastNewlineEnd = start;
143
+ let lineStart = 0;
144
+ for (let i = 0; i < buffer.length; i++) {
145
+ if (buffer[i] !== 0x0a) continue;
146
+
147
+ // Slice the line (trim a trailing \r for CRLF files), then advance the cursor past the \n
148
+ // regardless of whether the line yielded anything — we've fully observed those bytes.
149
+ const lineEnd = i > lineStart && buffer[i - 1] === 0x0d ? i - 1 : i;
150
+ if (lineEnd > lineStart) {
151
+ const line = buffer.toString('utf8', lineStart, lineEnd);
152
+ applyTitleLine(line, sessionId, titles);
153
+ }
154
+ lineStart = i + 1;
155
+ lastNewlineEnd = start + i + 1;
156
+ }
157
+
158
+ return { titles: titles, consumedEnd: lastNewlineEnd };
159
+ }
160
+ }
161
+
162
+ function applyTitleLine(line: string, sessionId: string, titles: TranscriptTitles): void {
163
+ if (!isLikelyTitleLine(line)) return;
164
+
165
+ let entry: TitleEntry;
166
+ try {
167
+ entry = JSON.parse(line) as TitleEntry;
168
+ } catch {
169
+ return;
170
+ }
171
+ if (entry.sessionId != null && entry.sessionId !== sessionId) return;
172
+
173
+ switch (entry.type) {
174
+ case 'custom-title':
175
+ if (typeof entry.customTitle === 'string' && entry.customTitle.length > 0) {
176
+ titles.custom = entry.customTitle;
177
+ }
178
+ break;
179
+ case 'ai-title':
180
+ if (typeof entry.aiTitle === 'string' && entry.aiTitle.length > 0) {
181
+ titles.ai = entry.aiTitle;
182
+ }
183
+ break;
184
+ case 'agent-name':
185
+ if (typeof entry.agentName === 'string' && entry.agentName.length > 0) {
186
+ titles.agent = entry.agentName;
187
+ }
188
+ break;
189
+ }
190
+ }
191
+
192
+ function isLikelyTitleLine(line: string): boolean {
193
+ if (!line.includes('"type"')) return false;
194
+ return line.includes('-title') || line.includes('agent-name');
195
+ }
196
+
197
+ async function fileExists(path: string): Promise<boolean> {
198
+ try {
199
+ const stats = await stat(path);
200
+ return stats.isFile();
201
+ } catch {
202
+ return false;
203
+ }
204
+ }
205
+
206
+ function readSlice(path: string, start: number, end: number): Promise<Buffer> {
207
+ return new Promise((resolve, reject) => {
208
+ const chunks: Buffer[] = [];
209
+ const stream = createReadStream(path, { start: start, end: end - 1 });
210
+ stream.on('data', chunk => chunks.push(chunk as Buffer));
211
+ stream.on('end', () => resolve(Buffer.concat(chunks)));
212
+ stream.on('error', reject);
213
+ });
214
+ }
@@ -0,0 +1,109 @@
1
+ import { truncate } from '../../utils/string.js';
2
+
3
+ const maxStoredPromptLength = 500;
4
+
5
+ // Marker blocks GitLens itself embeds when dispatching Start Work / Start Review prompts to an
6
+ // agent (see `start-work-issue` / `start-review-pullRequest` templates in
7
+ // `packages/plus/ai/src/prompts.ts`). The surrounding template begins with a long
8
+ // "You are an advanced AI programming assistant…" preamble that would otherwise become the
9
+ // session's display name; matching on the block delimiters lets us pull the issue/PR title out
10
+ // of the embedded JSON instead.
11
+ const dispatchBlockRegex = /<(issue|prData)>([\s\S]*?)<\/\1>/;
12
+
13
+ // Wrappers the Claude Code harness or its VS Code extension prepend to the `prompt` field of
14
+ // `UserPromptSubmit` hook events. These are synthetic context blocks, not user-typed content,
15
+ // and shouldn't appear verbatim in GitLens agent status UI.
16
+ const harnessBlockPatterns: readonly RegExp[] = [
17
+ // VS Code extension: any current or future IDE-context tag (e.g. <ide_opened_file>,
18
+ // <ide_selection>). Prefix-matched so newly-added ones are handled automatically.
19
+ /<(ide_[a-zA-Z0-9_]+)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g,
20
+ // VS Code extension: @terminal:<name> mention expansion. Gated on the `name` attribute so we
21
+ // don't strip a bare <terminal> a user pasted as code.
22
+ /<terminal\s+name="[^"]*"[^>]*>[\s\S]*?<\/terminal>/g,
23
+ // VS Code extension: @browser mention expansion. Gated on tabGroupId/tabId for the same reason.
24
+ /<browser\s+[^>]*\b(?:tabGroupId|tabId)="[^"]*"[^>]*>[\s\S]*?<\/browser>/g,
25
+ // VS Code extension: one-shot system instruction prepended whenever any @browser is present.
26
+ /<browser_instruction>[\s\S]*?<\/browser_instruction>/g,
27
+ // CLI harness: background-bash task lifecycle event (status, summary, output-file, etc.).
28
+ /<task-notification>[\s\S]*?<\/task-notification>/g,
29
+ // CLI harness: built-in slash-command stdout/stderr echo and caveat note.
30
+ /<(local-command-(?:stdout|stderr|caveat))>[\s\S]*?<\/\1>/g,
31
+ // CLI harness: bang-prefix bash invocation echo (input/stdout/stderr).
32
+ /<(bash-(?:input|stdout|stderr))>[\s\S]*?<\/\1>/g,
33
+ // CLI harness: tool-permission response wrapper.
34
+ /<permissionresponse>[\s\S]*?<\/permissionresponse>/g,
35
+ // Anthropic shared-context blocks (claude.ai web-app feature; defensive).
36
+ /<shared-context(?:\s[^>]*)?>[\s\S]*?<\/shared-context>/g,
37
+ ];
38
+
39
+ /**
40
+ * Normalizes a Claude Code `UserPromptSubmit` event prompt into something fit for display in
41
+ * GitLens (agent status pill, sidebar tooltip, etc.) by stripping harness-injected wrappers.
42
+ *
43
+ * Returns `undefined` for empty input or when the prompt was nothing but harness context, so the
44
+ * caller can skip overwriting the previously-captured user prompt.
45
+ */
46
+ export function sanitizeAgentPrompt(prompt: string | undefined): string | undefined {
47
+ if (!prompt) return undefined;
48
+
49
+ // Normalize CRLF up-front so the harness-block and whitespace-collapse regexes (which target
50
+ // `\n`) behave identically for Windows-originated prompts.
51
+ let result = prompt.replace(/\r\n/g, '\n');
52
+
53
+ let stripped = false;
54
+ for (const pattern of harnessBlockPatterns) {
55
+ const next = result.replace(pattern, '');
56
+ if (next !== result) {
57
+ stripped = true;
58
+ result = next;
59
+ }
60
+ }
61
+
62
+ // GitLens-dispatched Start Work / Start Review prompts: replace the template body with just
63
+ // the issue/PR title so the session name doesn't surface the "You are an advanced AI…"
64
+ // preamble. Runs AFTER harness stripping so that an `<ide_selection>` (or other wrapper)
65
+ // containing an `<issue>…</issue>` block doesn't get rewritten to the embedded title — the
66
+ // wrapper is stripped first and only a genuine dispatch template body triggers the rewrite.
67
+ // Anything that doesn't yield a usable title falls through to normal sanitization.
68
+ const dispatchTitle = extractDispatchTitle(result);
69
+ if (dispatchTitle != null) return dispatchTitle;
70
+
71
+ // Only collapse the whitespace runs around stripped blocks. Untouched user prompts pass
72
+ // through with their internal whitespace intact (the final trim still removes the outer
73
+ // padding either way).
74
+ if (stripped) {
75
+ result = result.replace(/[ \t]+\n/g, '\n').replace(/\n{3,}/g, '\n\n');
76
+ }
77
+ result = result.trim();
78
+ return result || undefined;
79
+ }
80
+
81
+ /**
82
+ * Sanitizes and truncates a raw prompt for storage on an `AgentSession`. Use this everywhere a
83
+ * session's `lastPrompt` / `firstPrompt` is populated so the sanitize+truncate pipeline can't be
84
+ * applied inconsistently across capture paths (hook events, CLI session-state syncs, etc.).
85
+ */
86
+ export function prepareStoredPrompt(prompt: string | undefined): string | undefined {
87
+ const cleaned = sanitizeAgentPrompt(prompt);
88
+ return cleaned ? truncate(cleaned, maxStoredPromptLength) : undefined;
89
+ }
90
+
91
+ /**
92
+ * Looks for a GitLens-dispatch marker block (`<issue>…</issue>` or `<prData>…</prData>`) and
93
+ * returns the embedded `title` field. Both `IssueShape` and `PullRequestShape` carry `title`, so
94
+ * a single accessor covers Start Work and Start Review. Returns `undefined` when the block is
95
+ * absent, the JSON fails to parse, or `title` is missing/empty — those cases fall through to the
96
+ * regular sanitization path.
97
+ */
98
+ function extractDispatchTitle(prompt: string): string | undefined {
99
+ const match = dispatchBlockRegex.exec(prompt);
100
+ if (match == null) return undefined;
101
+
102
+ try {
103
+ const parsed = JSON.parse(match[2]) as { title?: unknown };
104
+ const title = typeof parsed.title === 'string' ? parsed.title.trim() : '';
105
+ return title.length > 0 ? title : undefined;
106
+ } catch {
107
+ return undefined;
108
+ }
109
+ }
@@ -0,0 +1,197 @@
1
+ import { kill } from 'process';
2
+ import type { AgentSession, AgentSessionStatus, PendingPermissionKind } from './types.js';
3
+
4
+ /** Cap for derived plan / question summaries — short enough to clamp into a card row without
5
+ * truncation eating the leading content, long enough to convey what the prompt is about. */
6
+ const summaryMaxLength = 120;
7
+
8
+ export function deriveStatusFromEvent(event: string): AgentSessionStatus {
9
+ switch (event) {
10
+ case 'SessionStart':
11
+ case 'Stop':
12
+ case 'StopFailure':
13
+ return 'idle';
14
+ case 'PreToolUse':
15
+ return 'tool_use';
16
+ case 'PreCompact':
17
+ return 'compacting';
18
+ case 'PermissionRequest':
19
+ case 'Elicitation':
20
+ return 'permission_requested';
21
+ case 'UserPromptSubmit':
22
+ case 'PostToolUse':
23
+ case 'PostToolUseFailure':
24
+ case 'PostCompact':
25
+ case 'PermissionDenied':
26
+ case 'ElicitationResult':
27
+ case 'SubagentStart':
28
+ case 'SubagentStop':
29
+ return 'thinking';
30
+ default:
31
+ return 'idle';
32
+ }
33
+ }
34
+
35
+ export function isProcessAlive(pid: number): boolean {
36
+ // `kill(0, ...)` and `kill(<negative>, ...)` have process-group semantics on POSIX
37
+ // (and target the current process under libuv on Windows), so they don't tell us
38
+ // anything about a specific pid. Reject anything that isn't a real, positive pid.
39
+ if (!Number.isInteger(pid) || pid <= 0) return false;
40
+
41
+ try {
42
+ kill(pid, 0);
43
+ return true;
44
+ } catch (ex) {
45
+ // The process exists but we can't signal it: sandboxed/different uid (POSIX
46
+ // EPERM) or `OpenProcess` access-denied (Windows EACCES). Treat as alive.
47
+ // ESRCH and everything else mean dead.
48
+ const code = (ex as NodeJS.ErrnoException)?.code;
49
+ return code === 'EPERM' || code === 'EACCES';
50
+ }
51
+ }
52
+
53
+ export function rehydrateSubagents(
54
+ subagents:
55
+ | (Omit<AgentSession, 'lastActivity' | 'phaseSince'> & { lastActivity: string; phaseSince: string })[]
56
+ | undefined,
57
+ ): AgentSession[] | undefined {
58
+ if (subagents == null || subagents.length === 0) return undefined;
59
+ return subagents.map(s => ({
60
+ ...s,
61
+ lastActivity: new Date(s.lastActivity),
62
+ phaseSince: new Date(s.phaseSince),
63
+ }));
64
+ }
65
+
66
+ export function describeToolInput(toolName: string, toolInput: Record<string, unknown>): string {
67
+ let detail: string | undefined;
68
+ switch (toolName) {
69
+ case 'Bash':
70
+ detail = toolInput.command as string | undefined;
71
+ break;
72
+ case 'Edit':
73
+ case 'MultiEdit':
74
+ case 'Write':
75
+ case 'Read':
76
+ detail = toolInput.file_path as string | undefined;
77
+ break;
78
+ case 'Grep':
79
+ case 'Glob':
80
+ detail = toolInput.pattern as string | undefined;
81
+ break;
82
+ case 'WebFetch':
83
+ detail = toolInput.url as string | undefined;
84
+ break;
85
+ case 'WebSearch':
86
+ detail = toolInput.query as string | undefined;
87
+ break;
88
+ case 'NotebookEdit':
89
+ detail = toolInput.notebook_path as string | undefined;
90
+ break;
91
+ case 'ExitPlanMode':
92
+ detail = extractPlanSummary(toolInput);
93
+ break;
94
+ case 'AskUserQuestion':
95
+ detail = extractQuestionDetails(toolInput)?.text;
96
+ break;
97
+ }
98
+
99
+ return detail != null ? `${toolName}(${detail})` : toolName;
100
+ }
101
+
102
+ /** Maps a Claude Code tool name to its UX-relevant prompt kind. `ExitPlanMode` and
103
+ * `AskUserQuestion` are the two tools whose semantics differ from a regular permission
104
+ * request — everything else falls through to the generic tool kind. */
105
+ export function classifyPermissionKind(toolName: string): PendingPermissionKind {
106
+ switch (toolName) {
107
+ case 'ExitPlanMode':
108
+ return 'plan';
109
+ case 'AskUserQuestion':
110
+ return 'question';
111
+ default:
112
+ return 'tool';
113
+ }
114
+ }
115
+
116
+ /** Extracts a short, scannable summary from an ExitPlanMode plan body. Prefers the first
117
+ * Markdown heading (stripped of leading `#`s) so the plan's title surfaces; falls back to
118
+ * the first non-empty line if no heading appears. Capped at {@link summaryMaxLength} with an
119
+ * ellipsis. */
120
+ export function extractPlanSummary(toolInput: Record<string, unknown>): string | undefined {
121
+ const plan = toolInput.plan as string | undefined;
122
+ if (!plan) return undefined;
123
+
124
+ let heading: string | undefined;
125
+ let firstNonEmpty: string | undefined;
126
+ for (const line of plan.split(/\r?\n/)) {
127
+ const trimmed = line.trim();
128
+ if (!trimmed) continue;
129
+
130
+ if (trimmed.startsWith('#')) {
131
+ const stripped = trimmed.replace(/^#+\s*/, '').trim();
132
+ if (stripped) {
133
+ heading = stripped;
134
+ break;
135
+ }
136
+ }
137
+
138
+ firstNonEmpty ??= trimmed;
139
+ }
140
+
141
+ const summary = heading ?? firstNonEmpty;
142
+ if (!summary) return undefined;
143
+
144
+ return summary.length > summaryMaxLength ? `${summary.slice(0, summaryMaxLength).trimEnd()}…` : summary;
145
+ }
146
+
147
+ /** Extracts the leading question text + total question count from an AskUserQuestion call.
148
+ * Returns undefined if the input doesn't carry a recognisable `questions` array. */
149
+ export function extractQuestionDetails(
150
+ toolInput: Record<string, unknown>,
151
+ ): { text: string; count: number } | undefined {
152
+ const questions = toolInput.questions as ReadonlyArray<Record<string, unknown>> | undefined;
153
+ if (!Array.isArray(questions) || questions.length === 0) return undefined;
154
+
155
+ const first = questions[0]?.question;
156
+ if (typeof first !== 'string') return undefined;
157
+
158
+ const trimmed = first.trim();
159
+ if (!trimmed) return undefined;
160
+
161
+ const text = trimmed.length > summaryMaxLength ? `${trimmed.slice(0, summaryMaxLength).trimEnd()}…` : trimmed;
162
+ return { text: text, count: questions.length };
163
+ }
164
+
165
+ /** Returns the file path a file-mutating tool is targeting, or `undefined` for non-mutating /
166
+ * non-file tools. `Read` is intentionally excluded — "working on a file" means writing to it.
167
+ * Reads are surfaced separately via {@link getToolReadPath} so consumers (e.g. the treemap
168
+ * activity overlay) can render reads with a distinct visual treatment. */
169
+ export function getToolFilePath(toolName: string, toolInput: Record<string, unknown> | undefined): string | undefined {
170
+ if (toolInput == null) return undefined;
171
+
172
+ switch (toolName) {
173
+ case 'Edit':
174
+ case 'MultiEdit':
175
+ case 'Write':
176
+ return toolInput.file_path as string | undefined;
177
+ case 'NotebookEdit':
178
+ return toolInput.notebook_path as string | undefined;
179
+ default:
180
+ return undefined;
181
+ }
182
+ }
183
+
184
+ /** Returns the file path a read-only file tool is targeting, or `undefined` for non-read /
185
+ * non-file tools. Mirrors {@link getToolFilePath} but for `Read`/`NotebookRead`. */
186
+ export function getToolReadPath(toolName: string, toolInput: Record<string, unknown> | undefined): string | undefined {
187
+ if (toolInput == null) return undefined;
188
+
189
+ switch (toolName) {
190
+ case 'Read':
191
+ return toolInput.file_path as string | undefined;
192
+ case 'NotebookRead':
193
+ return toolInput.notebook_path as string | undefined;
194
+ default:
195
+ return undefined;
196
+ }
197
+ }