@agimon-ai/doompi-git 0.0.1-alpha.8 → 0.0.1-alpha.9

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 (323) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +23 -1
  2. package/dist/api-contracts.cjs +7 -1
  3. package/dist/api-contracts.d.cts +1 -1
  4. package/dist/api-contracts.d.mts +1 -1
  5. package/dist/api-contracts.mjs +2 -1
  6. package/dist/extensions/pi.cjs +38 -1
  7. package/dist/extensions/pi.cjs.map +1 -1
  8. package/dist/extensions/pi.d.cts +5 -4
  9. package/dist/extensions/pi.d.cts.map +1 -1
  10. package/dist/extensions/pi.d.mts +5 -4
  11. package/dist/extensions/pi.d.mts.map +1 -1
  12. package/dist/extensions/pi.mjs +33 -1
  13. package/dist/extensions/pi.mjs.map +1 -1
  14. package/dist/extensions/server.cjs +72 -1
  15. package/dist/extensions/server.cjs.map +1 -1
  16. package/dist/extensions/server.d.cts +3 -3
  17. package/dist/extensions/server.d.cts.map +1 -1
  18. package/dist/extensions/server.d.mts +3 -3
  19. package/dist/extensions/server.d.mts.map +1 -1
  20. package/dist/extensions/server.mjs +67 -1
  21. package/dist/extensions/server.mjs.map +1 -1
  22. package/dist/extensions/web.mjs +485 -0
  23. package/dist/extensions/web.mjs.map +1 -0
  24. package/dist/index.cjs +5 -1
  25. package/dist/index.d.cts +3 -3
  26. package/dist/index.d.mts +3 -3
  27. package/dist/index.mjs +3 -1
  28. package/dist/src/constants/git.cjs +8 -0
  29. package/dist/src/constants/git.cjs.map +1 -0
  30. package/dist/src/constants/git.mjs +7 -0
  31. package/dist/src/constants/git.mjs.map +1 -0
  32. package/dist/src/extensions/(backend)/api/_lib/route.server.cjs +93 -0
  33. package/dist/src/extensions/(backend)/api/_lib/route.server.cjs.map +1 -0
  34. package/dist/src/extensions/(backend)/api/_lib/route.server.mjs +92 -0
  35. package/dist/src/extensions/(backend)/api/_lib/route.server.mjs.map +1 -0
  36. package/dist/src/extensions/(backend)/api/route.server.cjs +7 -0
  37. package/dist/src/extensions/(backend)/api/route.server.cjs.map +1 -0
  38. package/dist/src/extensions/(backend)/api/route.server.mjs +8 -0
  39. package/dist/src/extensions/(backend)/api/route.server.mjs.map +1 -0
  40. package/dist/src/extensions/(backend)/channel/_lib/git-worktrees.server.cjs +195 -0
  41. package/dist/src/extensions/(backend)/channel/_lib/git-worktrees.server.cjs.map +1 -0
  42. package/dist/src/extensions/(backend)/channel/_lib/git-worktrees.server.mjs +194 -0
  43. package/dist/src/extensions/(backend)/channel/_lib/git-worktrees.server.mjs.map +1 -0
  44. package/dist/src/extensions/(backend)/channel/git-worktrees.server.cjs +7 -0
  45. package/dist/src/extensions/(backend)/channel/git-worktrees.server.cjs.map +1 -0
  46. package/dist/src/extensions/(backend)/channel/git-worktrees.server.mjs +8 -0
  47. package/dist/src/extensions/(backend)/channel/git-worktrees.server.mjs.map +1 -0
  48. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.cli.cjs +7 -0
  49. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.cli.cjs.map +1 -0
  50. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.cli.mjs +8 -0
  51. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.cli.mjs.map +1 -0
  52. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.server.cjs +71 -0
  53. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.server.cjs.map +1 -0
  54. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.server.mjs +70 -0
  55. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/root.server.mjs.map +1 -0
  56. package/dist/src/extensions/workspaces/sessions/(backend)/command/_lib/git.cli.cjs +17 -0
  57. package/dist/src/extensions/workspaces/sessions/(backend)/command/_lib/git.cli.cjs.map +1 -0
  58. package/dist/src/extensions/workspaces/sessions/(backend)/command/_lib/git.cli.mjs +16 -0
  59. package/dist/src/extensions/workspaces/sessions/(backend)/command/_lib/git.cli.mjs.map +1 -0
  60. package/dist/src/extensions/workspaces/sessions/(backend)/command/git.cli.cjs +7 -0
  61. package/dist/src/extensions/workspaces/sessions/(backend)/command/git.cli.cjs.map +1 -0
  62. package/dist/src/extensions/workspaces/sessions/(backend)/command/git.cli.mjs +8 -0
  63. package/dist/src/extensions/workspaces/sessions/(backend)/command/git.cli.mjs.map +1 -0
  64. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompi-use-git.cli.cjs +15 -0
  65. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompi-use-git.cli.cjs.map +1 -0
  66. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompi-use-git.cli.mjs +15 -0
  67. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompi-use-git.cli.mjs.map +1 -0
  68. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGit.server.cjs +6 -0
  69. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGit.server.cjs.map +1 -0
  70. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGit.server.mjs +6 -0
  71. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGit.server.mjs.map +1 -0
  72. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGitReadme.server.cjs +6 -0
  73. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGitReadme.server.cjs.map +1 -0
  74. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGitReadme.server.mjs +6 -0
  75. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiGitReadme.server.mjs.map +1 -0
  76. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiUseGit.server.cjs +6 -0
  77. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiUseGit.server.cjs.map +1 -0
  78. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiUseGit.server.mjs +6 -0
  79. package/dist/src/extensions/workspaces/sessions/(backend)/resource/_lib/doompiUseGit.server.mjs.map +1 -0
  80. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-git.cli.cjs +7 -0
  81. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-git.cli.cjs.map +1 -0
  82. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-git.cli.mjs +8 -0
  83. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-git.cli.mjs.map +1 -0
  84. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGit.server.cjs +7 -0
  85. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGit.server.cjs.map +1 -0
  86. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGit.server.mjs +8 -0
  87. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGit.server.mjs.map +1 -0
  88. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGitReadme.server.cjs +7 -0
  89. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGitReadme.server.cjs.map +1 -0
  90. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGitReadme.server.mjs +8 -0
  91. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiGitReadme.server.mjs.map +1 -0
  92. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiUseGit.server.cjs +7 -0
  93. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiUseGit.server.cjs.map +1 -0
  94. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiUseGit.server.mjs +8 -0
  95. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompiUseGit.server.mjs.map +1 -0
  96. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs +7 -0
  97. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs.map +1 -0
  98. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs +8 -0
  99. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs.map +1 -0
  100. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +7 -0
  101. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +1 -0
  102. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +8 -0
  103. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +1 -0
  104. package/dist/src/extensions/workspaces/sessions/(backend)/tool/_lib/runWorktree.cli.cjs +38 -0
  105. package/dist/src/extensions/workspaces/sessions/(backend)/tool/_lib/runWorktree.cli.cjs.map +1 -0
  106. package/dist/src/extensions/workspaces/sessions/(backend)/tool/_lib/runWorktree.cli.mjs +36 -0
  107. package/dist/src/extensions/workspaces/sessions/(backend)/tool/_lib/runWorktree.cli.mjs.map +1 -0
  108. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.cli.cjs +7 -0
  109. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.cli.cjs.map +1 -0
  110. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.cli.mjs +8 -0
  111. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.cli.mjs.map +1 -0
  112. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.server.cjs +6 -0
  113. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.server.cjs.map +1 -0
  114. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.server.mjs +7 -0
  115. package/dist/src/extensions/workspaces/sessions/(backend)/tool/runWorktree.server.mjs.map +1 -0
  116. package/dist/src/schemas/apiContracts.cjs +138 -0
  117. package/dist/src/schemas/apiContracts.cjs.map +1 -0
  118. package/dist/{schemas → src/schemas}/apiContracts.d.cts.map +1 -1
  119. package/dist/{schemas → src/schemas}/apiContracts.d.mts.map +1 -1
  120. package/dist/src/schemas/apiContracts.mjs +135 -0
  121. package/dist/src/schemas/apiContracts.mjs.map +1 -0
  122. package/dist/src/schemas/runWorktreeTool.cjs +152 -0
  123. package/dist/src/schemas/runWorktreeTool.cjs.map +1 -0
  124. package/dist/src/schemas/runWorktreeTool.mjs +150 -0
  125. package/dist/src/schemas/runWorktreeTool.mjs.map +1 -0
  126. package/dist/src/services/atomicJson/index.cjs +42 -0
  127. package/dist/src/services/atomicJson/index.cjs.map +1 -0
  128. package/dist/src/services/atomicJson/index.mjs +38 -0
  129. package/dist/src/services/atomicJson/index.mjs.map +1 -0
  130. package/dist/src/services/compositionMirror/index.cjs +119 -0
  131. package/dist/src/services/compositionMirror/index.cjs.map +1 -0
  132. package/dist/src/services/compositionMirror/index.mjs +116 -0
  133. package/dist/src/services/compositionMirror/index.mjs.map +1 -0
  134. package/dist/src/services/errors/index.cjs +29 -0
  135. package/dist/src/services/errors/index.cjs.map +1 -0
  136. package/dist/src/services/errors/index.mjs +27 -0
  137. package/dist/src/services/errors/index.mjs.map +1 -0
  138. package/dist/src/services/extensionService/index.cjs +13 -0
  139. package/dist/src/services/extensionService/index.cjs.map +1 -0
  140. package/dist/src/services/extensionService/index.d.cts.map +1 -0
  141. package/dist/src/services/extensionService/index.d.mts.map +1 -0
  142. package/dist/src/services/extensionService/index.mjs +13 -0
  143. package/dist/src/services/extensionService/index.mjs.map +1 -0
  144. package/dist/src/services/gitCli/index.cjs +144 -0
  145. package/dist/src/services/gitCli/index.cjs.map +1 -0
  146. package/dist/src/services/gitCli/index.mjs +142 -0
  147. package/dist/src/services/gitCli/index.mjs.map +1 -0
  148. package/dist/src/services/gitDependencies/index.cjs +14 -0
  149. package/dist/src/services/gitDependencies/index.cjs.map +1 -0
  150. package/dist/src/services/gitDependencies/index.d.cts.map +1 -0
  151. package/dist/src/services/gitDependencies/index.d.mts.map +1 -0
  152. package/dist/src/services/gitDependencies/index.mjs +14 -0
  153. package/dist/src/services/gitDependencies/index.mjs.map +1 -0
  154. package/dist/src/services/paths/index.cjs +35 -0
  155. package/dist/src/services/paths/index.cjs.map +1 -0
  156. package/dist/src/services/paths/index.mjs +30 -0
  157. package/dist/src/services/paths/index.mjs.map +1 -0
  158. package/dist/src/services/repositoryIdentity/index.cjs +71 -0
  159. package/dist/src/services/repositoryIdentity/index.cjs.map +1 -0
  160. package/dist/src/services/repositoryIdentity/index.mjs +65 -0
  161. package/dist/src/services/repositoryIdentity/index.mjs.map +1 -0
  162. package/dist/src/services/runWorktree/index.cjs +145 -0
  163. package/dist/src/services/runWorktree/index.cjs.map +1 -0
  164. package/dist/src/services/runWorktree/index.mjs +142 -0
  165. package/dist/src/services/runWorktree/index.mjs.map +1 -0
  166. package/dist/src/services/worktreeEvents/index.cjs +64 -0
  167. package/dist/src/services/worktreeEvents/index.cjs.map +1 -0
  168. package/dist/src/services/worktreeEvents/index.d.cts.map +1 -0
  169. package/dist/src/services/worktreeEvents/index.d.mts.map +1 -0
  170. package/dist/src/services/worktreeEvents/index.mjs +60 -0
  171. package/dist/src/services/worktreeEvents/index.mjs.map +1 -0
  172. package/dist/src/services/worktreeNaming/index.cjs +99 -0
  173. package/dist/src/services/worktreeNaming/index.cjs.map +1 -0
  174. package/dist/src/services/worktreeNaming/index.d.cts.map +1 -0
  175. package/dist/src/services/worktreeNaming/index.d.mts.map +1 -0
  176. package/dist/src/services/worktreeNaming/index.mjs +94 -0
  177. package/dist/src/services/worktreeNaming/index.mjs.map +1 -0
  178. package/dist/src/services/worktreeOperations/index.cjs +263 -0
  179. package/dist/src/services/worktreeOperations/index.cjs.map +1 -0
  180. package/dist/src/services/worktreeOperations/index.d.cts.map +1 -0
  181. package/dist/src/services/worktreeOperations/index.d.mts.map +1 -0
  182. package/dist/src/services/worktreeOperations/index.mjs +261 -0
  183. package/dist/src/services/worktreeOperations/index.mjs.map +1 -0
  184. package/dist/src/services/worktreeRegistry/index.cjs +38 -0
  185. package/dist/src/services/worktreeRegistry/index.cjs.map +1 -0
  186. package/dist/src/services/worktreeRegistry/index.mjs +38 -0
  187. package/dist/src/services/worktreeRegistry/index.mjs.map +1 -0
  188. package/dist/src/types/extension.d.cts.map +1 -0
  189. package/dist/src/types/extension.d.mts.map +1 -0
  190. package/dist/src/types/webWorktrees.cjs +20 -0
  191. package/dist/src/types/webWorktrees.cjs.map +1 -0
  192. package/dist/src/types/webWorktrees.mjs +20 -0
  193. package/dist/src/types/webWorktrees.mjs.map +1 -0
  194. package/dist/src/types/worktreeRegistry.d.cts.map +1 -0
  195. package/dist/src/types/worktreeRegistry.d.mts.map +1 -0
  196. package/package.json +24 -19
  197. package/dist/constants/git.cjs +0 -2
  198. package/dist/constants/git.cjs.map +0 -1
  199. package/dist/constants/git.mjs +0 -2
  200. package/dist/constants/git.mjs.map +0 -1
  201. package/dist/controllers/gitCommand.cjs +0 -2
  202. package/dist/controllers/gitCommand.cjs.map +0 -1
  203. package/dist/controllers/gitCommand.mjs +0 -2
  204. package/dist/controllers/gitCommand.mjs.map +0 -1
  205. package/dist/controllers/gitSession.cjs +0 -2
  206. package/dist/controllers/gitSession.cjs.map +0 -1
  207. package/dist/controllers/gitSession.mjs +0 -2
  208. package/dist/controllers/gitSession.mjs.map +0 -1
  209. package/dist/controllers/hubApi.cjs +0 -2
  210. package/dist/controllers/hubApi.cjs.map +0 -1
  211. package/dist/controllers/hubApi.mjs +0 -2
  212. package/dist/controllers/hubApi.mjs.map +0 -1
  213. package/dist/controllers/worktreesChannel.cjs +0 -2
  214. package/dist/controllers/worktreesChannel.cjs.map +0 -1
  215. package/dist/controllers/worktreesChannel.mjs +0 -2
  216. package/dist/controllers/worktreesChannel.mjs.map +0 -1
  217. package/dist/schemas/apiContracts.cjs +0 -2
  218. package/dist/schemas/apiContracts.cjs.map +0 -1
  219. package/dist/schemas/apiContracts.mjs +0 -2
  220. package/dist/schemas/apiContracts.mjs.map +0 -1
  221. package/dist/schemas/runWorktreeTool.cjs +0 -2
  222. package/dist/schemas/runWorktreeTool.cjs.map +0 -1
  223. package/dist/schemas/runWorktreeTool.mjs +0 -2
  224. package/dist/schemas/runWorktreeTool.mjs.map +0 -1
  225. package/dist/services/atomicJson/index.cjs +0 -2
  226. package/dist/services/atomicJson/index.cjs.map +0 -1
  227. package/dist/services/atomicJson/index.mjs +0 -2
  228. package/dist/services/atomicJson/index.mjs.map +0 -1
  229. package/dist/services/compositionMirror/index.cjs +0 -2
  230. package/dist/services/compositionMirror/index.cjs.map +0 -1
  231. package/dist/services/compositionMirror/index.mjs +0 -2
  232. package/dist/services/compositionMirror/index.mjs.map +0 -1
  233. package/dist/services/errors/index.cjs +0 -2
  234. package/dist/services/errors/index.cjs.map +0 -1
  235. package/dist/services/errors/index.mjs +0 -2
  236. package/dist/services/errors/index.mjs.map +0 -1
  237. package/dist/services/extensionService/index.cjs +0 -2
  238. package/dist/services/extensionService/index.cjs.map +0 -1
  239. package/dist/services/extensionService/index.d.cts.map +0 -1
  240. package/dist/services/extensionService/index.d.mts.map +0 -1
  241. package/dist/services/extensionService/index.mjs +0 -2
  242. package/dist/services/extensionService/index.mjs.map +0 -1
  243. package/dist/services/gitCli/index.cjs +0 -4
  244. package/dist/services/gitCli/index.cjs.map +0 -1
  245. package/dist/services/gitCli/index.mjs +0 -4
  246. package/dist/services/gitCli/index.mjs.map +0 -1
  247. package/dist/services/gitDependencies/index.cjs +0 -2
  248. package/dist/services/gitDependencies/index.cjs.map +0 -1
  249. package/dist/services/gitDependencies/index.d.cts.map +0 -1
  250. package/dist/services/gitDependencies/index.d.mts.map +0 -1
  251. package/dist/services/gitDependencies/index.mjs +0 -2
  252. package/dist/services/gitDependencies/index.mjs.map +0 -1
  253. package/dist/services/paths/index.cjs +0 -2
  254. package/dist/services/paths/index.cjs.map +0 -1
  255. package/dist/services/paths/index.mjs +0 -2
  256. package/dist/services/paths/index.mjs.map +0 -1
  257. package/dist/services/repositoryIdentity/index.cjs +0 -2
  258. package/dist/services/repositoryIdentity/index.cjs.map +0 -1
  259. package/dist/services/repositoryIdentity/index.mjs +0 -2
  260. package/dist/services/repositoryIdentity/index.mjs.map +0 -1
  261. package/dist/services/runWorktree/index.cjs +0 -15
  262. package/dist/services/runWorktree/index.cjs.map +0 -1
  263. package/dist/services/runWorktree/index.mjs +0 -15
  264. package/dist/services/runWorktree/index.mjs.map +0 -1
  265. package/dist/services/worktreeEvents/index.cjs +0 -2
  266. package/dist/services/worktreeEvents/index.cjs.map +0 -1
  267. package/dist/services/worktreeEvents/index.d.cts.map +0 -1
  268. package/dist/services/worktreeEvents/index.d.mts.map +0 -1
  269. package/dist/services/worktreeEvents/index.mjs +0 -2
  270. package/dist/services/worktreeEvents/index.mjs.map +0 -1
  271. package/dist/services/worktreeNaming/index.cjs +0 -2
  272. package/dist/services/worktreeNaming/index.cjs.map +0 -1
  273. package/dist/services/worktreeNaming/index.d.cts.map +0 -1
  274. package/dist/services/worktreeNaming/index.d.mts.map +0 -1
  275. package/dist/services/worktreeNaming/index.mjs +0 -2
  276. package/dist/services/worktreeNaming/index.mjs.map +0 -1
  277. package/dist/services/worktreeOperations/index.cjs +0 -2
  278. package/dist/services/worktreeOperations/index.cjs.map +0 -1
  279. package/dist/services/worktreeOperations/index.d.cts.map +0 -1
  280. package/dist/services/worktreeOperations/index.d.mts.map +0 -1
  281. package/dist/services/worktreeOperations/index.mjs +0 -2
  282. package/dist/services/worktreeOperations/index.mjs.map +0 -1
  283. package/dist/services/worktreeRegistry/index.cjs +0 -2
  284. package/dist/services/worktreeRegistry/index.cjs.map +0 -1
  285. package/dist/services/worktreeRegistry/index.mjs +0 -2
  286. package/dist/services/worktreeRegistry/index.mjs.map +0 -1
  287. package/dist/tools/runWorktree.cjs +0 -2
  288. package/dist/tools/runWorktree.cjs.map +0 -1
  289. package/dist/tools/runWorktree.mjs +0 -2
  290. package/dist/tools/runWorktree.mjs.map +0 -1
  291. package/dist/types/extension.d.cts.map +0 -1
  292. package/dist/types/extension.d.mts.map +0 -1
  293. package/dist/types/webWorktrees.cjs +0 -2
  294. package/dist/types/webWorktrees.cjs.map +0 -1
  295. package/dist/types/webWorktrees.mjs +0 -2
  296. package/dist/types/webWorktrees.mjs.map +0 -1
  297. package/dist/types/worktreeRegistry.d.cts.map +0 -1
  298. package/dist/types/worktreeRegistry.d.mts.map +0 -1
  299. package/src/extensions/web.ts +0 -33
  300. package/src/types/webWorktrees.ts +0 -55
  301. package/src/types/worktreeRegistry.ts +0 -78
  302. package/src/web/components/RunWorktreeToolMessage.tsx +0 -45
  303. package/src/web/components/WorktreesActivitySection.tsx +0 -80
  304. package/src/web/components/WorktreesPanel.tsx +0 -175
  305. package/src/web/components/toolRenderers.ts +0 -8
  306. package/src/web/stores/worktreesActivityStore.ts +0 -125
  307. package/src/web/tsconfig.json +0 -22
  308. /package/dist/{schemas → src/schemas}/apiContracts.d.cts +0 -0
  309. /package/dist/{schemas → src/schemas}/apiContracts.d.mts +0 -0
  310. /package/dist/{services → src/services}/extensionService/index.d.cts +0 -0
  311. /package/dist/{services → src/services}/extensionService/index.d.mts +0 -0
  312. /package/dist/{services → src/services}/gitDependencies/index.d.cts +0 -0
  313. /package/dist/{services → src/services}/gitDependencies/index.d.mts +0 -0
  314. /package/dist/{services → src/services}/worktreeEvents/index.d.cts +0 -0
  315. /package/dist/{services → src/services}/worktreeEvents/index.d.mts +0 -0
  316. /package/dist/{services → src/services}/worktreeNaming/index.d.cts +0 -0
  317. /package/dist/{services → src/services}/worktreeNaming/index.d.mts +0 -0
  318. /package/dist/{services → src/services}/worktreeOperations/index.d.cts +0 -0
  319. /package/dist/{services → src/services}/worktreeOperations/index.d.mts +0 -0
  320. /package/dist/{types → src/types}/extension.d.cts +0 -0
  321. /package/dist/{types → src/types}/extension.d.mts +0 -0
  322. /package/dist/{types → src/types}/worktreeRegistry.d.cts +0 -0
  323. /package/dist/{types → src/types}/worktreeRegistry.d.mts +0 -0
@@ -1,2 +0,0 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../compositionMirror/index.cjs"),n=require("../errors/index.cjs"),r=require("../repositoryIdentity/index.cjs"),i=require("../paths/index.cjs"),a=require("../worktreeEvents/index.cjs"),o=require("../worktreeNaming/index.cjs"),s=require("../worktreeRegistry/index.cjs");let c=require("node:fs");c=e.__toESM(c,1);let l=require("node:crypto");function u(e){let{git:u}=e,d=e.sessionService,f=e.mirror??t.mirrorComposition,p=e.now??(()=>new Date),m=()=>{if(d===void 0)throw new n.HubUnavailableError(`The cockpit session service is unavailable. Start the cockpit and try again.`);return d},h=()=>{if(e.messageInbox===void 0)throw new n.HubUnavailableError(`The Git message service is unavailable. Start the cockpit and try again.`);return e.messageInbox},g={alive:e=>m().isLive(e.sessionId),exists:e=>c.default.existsSync(e)},_=async t=>{let r=await u.repositoryRoot(t.cwd);if(r===void 0)throw new n.DoomGitExpectedError(`not_a_repository`,`${t.cwd} is not inside a git repository.`,!1,`Run this from a repository checkout.`);let a=s.createWorktreeRegistry(i.registryFile(r,e.homeDir));return{root:r,store:a,records:a.list()}},v=(e,t)=>{let r=e.find(e=>e.id===t);if(r===void 0)throw new n.DoomGitExpectedError(`worktree_not_found`,`No worktree with id ${t}.`,!1,`Call list to see the current ids.`);return r},y=(e,t,r)=>{if(t.parentSessionId!==e.sessionId&&m().isLive(t.parentSessionId))throw new n.DoomGitExpectedError(`worktree_not_owned`,`Worktree ${t.id} belongs to another session.`,!1,`Ask the session that created it to ${r} it, or close that session first.`)},b=(e,t)=>{if(e.sessionId===t.parentSessionId)return{sessionId:t.sessionId,party:`parent`};if(e.sessionId===t.sessionId)return{sessionId:t.parentSessionId,party:`child`};throw new n.DoomGitExpectedError(`worktree_not_owned`,`Worktree ${t.id} belongs to another session.`,!1,`Use a worktree owned by this session.`)},x=e=>{if(!m().isLive(e))throw new n.DoomGitExpectedError(`worktree_peer_unavailable`,`The other worktree session is no longer live.`,!1,`Start a new worktree session before sending a message.`)};return{async spawn(t,a,s){let{root:c,store:d,records:h}=await _(t),g=o.refuseSpawn({branch:a.branch,existing:h});if(g!==void 0)throw new n.DoomGitExpectedError(`worktree_exists`,g,!1,`Pick another branch name, or close it.`);let v=a.baseRef??await u.currentBranch(c)??`HEAD`,y=r.shortId((0,l.randomUUID)()),b=o.worktreeDirectory({worktreesRoot:i.worktreesRoot(e.homeDir),repositoryLabel:r.repositoryLabel(c),repositoryId:r.repositoryId(c),branch:a.branch,shortId:y}),x=()=>s?.signal?.aborted===!0,S=async()=>(await u.removeWorktree({repositoryRoot:c,path:b,force:!0}).catch(()=>void 0),await u.deleteBranch({repositoryRoot:c,branch:a.branch}).catch(()=>!1)?``:` The branch ${a.branch} has work on it and was kept.`);if(s?.onProgress?.(`creating branch ${a.branch}\u2026`),await u.addWorktree({repositoryRoot:c,path:b,branch:a.branch,baseRef:v}),x()){let e=await S();throw new n.DoomGitExpectedError(`spawn_cancelled`,`Cancelled before the session started.${e}`,!1,`The worktree was removed; run it again when you are ready.`)}s?.onProgress?.(`mirroring build output…`),f(c,b),s?.onProgress?.(`starting session…`);let C;try{C=(await m().create({cwd:b,name:a.name??a.branch,parentSessionId:t.sessionId,sessionProvenance:`worktree`,...s?.signal===void 0?{}:{signal:s.signal}})).sessionId}catch(e){let t=await S();throw x()?new n.DoomGitExpectedError(`spawn_cancelled`,`Cancelled while the session was starting.${t}`,!1,`The worktree was removed; run it again when you are ready.`):e instanceof n.HubUnavailableError?new n.DoomGitExpectedError(`hub_unavailable`,`${e.message}${t}`,!0,`Start the cockpit and try again.`):e}let w={version:1,id:y,branch:a.branch,baseRef:v,path:b,repositoryRoot:c,sessionId:C,parentSessionId:t.sessionId,status:`running`,createdAt:p().toISOString()};try{d.replace([...h,w])}catch(e){await m().close(C).catch(()=>void 0);let t=await S();throw new n.DoomGitExpectedError(`registry_write_failed`,`The worktree was created but the registry could not be written (${e.message}).${t}`,!0,`Check the registry file for permissions or free space, then try again.`)}return w},async close(e,t,r){let{root:i,store:a,records:s}=await _(e),l=v(s,t);y(e,l,`close`);let d=c.default.existsSync(l.path)?await u.dirtyFiles(l.path):[],f=o.refuseClose({record:l,dirtyFiles:d,force:r});if(f!==void 0)throw new n.DoomGitExpectedError(`worktree_dirty`,f,!1,`Commit the work, or pass force: true.`);return await m().close(l.sessionId),await u.removeWorktree({repositoryRoot:i,path:l.path,force:r}),a.replace(s.filter(e=>e.id!==t)),l},async list(e){let{store:t,records:n}=await _(e),r=o.reconcile(n,g);return r.changed.length>0&&t.replace(r.records),r.records},async status(e,t){let{records:n}=await _(e),r=v(n,t);return{record:r,dirtyFiles:c.default.existsSync(r.path)?await u.dirtyFiles(r.path):[]}},async merge(e,t,r){let{root:i,records:a}=await _(e),o=v(a,t);y(e,o,`merge`);let s=await u.dirtyFiles(i);if(s.length>0)throw new n.DoomGitExpectedError(`worktree_dirty`,`The parent checkout has ${String(s.length)} uncommitted file(s): ${s.slice(0,10).join(`, `)}.`,!1,`Commit or stash the parent checkout first.`);return await u.mergeBranch({repositoryRoot:i,branch:o.branch,...r===void 0?{}:{message:r}}),o},async send(e,t,r){if(Buffer.byteLength(r,`utf8`)>65536)throw new n.DoomGitExpectedError(`message_too_large`,`A message may not exceed ${String(a.MAX_WORKTREE_MESSAGE_BYTES)} bytes.`,!1,`Send a shorter message.`);let{records:i}=await _(e),o=v(i,t),s=b(e,o);x(s.sessionId),h().send(s.sessionId,{version:1,worktreeId:o.id,fromSessionId:e.sessionId,from:s.party,text:r,sentAt:new Date().toISOString()})},async messages(e,t){let{records:n}=await _(e),r=v(n,t);return b(e,r),h().receive(r.id)},async prune(t,n){let{root:r,store:a,records:s}=await _(t),l=o.reconcile(s,g),d=await u.listWorktreePaths(r),f=new Map;for(let e of l.records)e.status===`orphaned`&&c.default.existsSync(e.path)&&f.set(e.path,await u.dirtyFiles(e.path));let p=o.planPrune({records:l.records,gitPaths:d,worktreesRoot:i.worktreesRoot(e.homeDir),dirtyByPath:f});if(n)return a.replace(l.records),p;for(let e of p.remove)await u.removeWorktree({repositoryRoot:r,path:e.path,force:!1});for(let e of[...p.remove,...p.forget])await u.deleteBranch({repositoryRoot:r,branch:e.branch}).catch(()=>!1)||p.keptBranches.push(e.branch);let m=new Set([...p.remove,...p.forget].map(e=>e.id));return a.replace(l.records.filter(e=>!m.has(e.id))),p}}}exports.createWorktreeOperations=u;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["mirrorComposition","HubUnavailableError","fs","DoomGitExpectedError","createWorktreeRegistry","registryFile","require","refuseSpawn","shortId","randomUUID","worktreeDirectory","worktreesRoot","repositoryLabel","repositoryId","refuseClose","reconcile","MAX_WORKTREE_MESSAGE_BYTES","planPrune"],"sources":["../../../src/services/worktreeOperations/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\n\nimport type { DoomHubSessionService } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { WORKTREE_RECORD_VERSION } from '../../types/worktreeRegistry';\nimport type { WorktreeGit, WorktreeRecord } from '../../types/worktreeRegistry';\nimport { mirrorComposition } from '../compositionMirror';\nimport { DoomGitExpectedError, HubUnavailableError } from '../errors';\nimport { registryFile, worktreesRoot } from '../paths';\nimport { repositoryId, repositoryLabel, shortId } from '../repositoryIdentity';\nimport { MAX_WORKTREE_MESSAGE_BYTES, type WorktreeMessageInbox, type WorktreeMessageParty } from '../worktreeEvents';\nimport { planPrune, reconcile, refuseClose, refuseSpawn, worktreeDirectory } from '../worktreeNaming';\nimport { createWorktreeRegistry } from '../worktreeRegistry';\n\nexport interface WorktreeContext {\n /** Where the calling session is working, used to find the repository. */\n cwd: string;\n /** The calling session, recorded as the new session's parent. */\n sessionId: string;\n}\n\nexport interface SpawnWorktreeRequest {\n branch: string;\n baseRef?: string;\n name?: string;\n}\n\nexport interface SpawnOptions {\n /** Aborted when the caller gives up, so a spawn nobody is waiting for stops. */\n signal?: AbortSignal;\n /** Phase labels, for a caller that can show them while the work runs. */\n onProgress?: (label: string) => void;\n}\n\nexport interface WorktreeOperations {\n spawn(context: WorktreeContext, request: SpawnWorktreeRequest, options?: SpawnOptions): Promise<WorktreeRecord>;\n close(context: WorktreeContext, id: string, force: boolean): Promise<WorktreeRecord>;\n list(context: WorktreeContext): Promise<WorktreeRecord[]>;\n status(context: WorktreeContext, id: string): Promise<{ record: WorktreeRecord; dirtyFiles: string[] }>;\n merge(context: WorktreeContext, id: string, message?: string): Promise<WorktreeRecord>;\n prune(context: WorktreeContext, dryRun: boolean): Promise<ReturnType<typeof planPrune>>;\n send(context: WorktreeContext, id: string, message: string): Promise<void>;\n messages(context: WorktreeContext, id: string): Promise<import('../worktreeEvents').WorktreeMessage[]>;\n}\n\nexport interface WorktreeOperationsDeps {\n git: WorktreeGit;\n /** The canonical hub-owned session lifecycle. */\n sessionService?: DoomHubSessionService;\n /** Session-local inbox for parent/worktree messages. */\n messageInbox?: WorktreeMessageInbox;\n /** Injected so a test never copies a real dependency tree. */\n mirror?: typeof mirrorComposition;\n homeDir?: string;\n /** Injected so a record's createdAt is reproducible in a test. */\n now?: () => Date;\n}\n\nexport function createWorktreeOperations(deps: WorktreeOperationsDeps): WorktreeOperations {\n const { git } = deps;\n const sessionService = deps.sessionService;\n const mirror = deps.mirror ?? mirrorComposition;\n const now = deps.now ?? (() => new Date());\n const requireSessionService = (): DoomHubSessionService => {\n if (sessionService === undefined)\n throw new HubUnavailableError('The cockpit session service is unavailable. Start the cockpit and try again.');\n return sessionService;\n };\n const requireMessageInbox = (): WorktreeMessageInbox => {\n if (deps.messageInbox === undefined)\n throw new HubUnavailableError('The Git message service is unavailable. Start the cockpit and try again.');\n return deps.messageInbox;\n };\n const probe = {\n alive: (record: WorktreeRecord) => requireSessionService().isLive(record.sessionId),\n exists: (path: string) => fs.existsSync(path),\n };\n\n /**\n * Everything an action needs about the repository it is acting on.\n *\n * Resolved per call rather than cached: a session's cwd is stable, but the\n * worktree registry is shared and another process may have written it since\n * the last call.\n */\n const resolve = async (context: WorktreeContext) => {\n const root = await git.repositoryRoot(context.cwd);\n if (root === undefined) {\n throw new DoomGitExpectedError(\n 'not_a_repository',\n `${context.cwd} is not inside a git repository.`,\n false,\n 'Run this from a repository checkout.',\n );\n }\n const store = createWorktreeRegistry(registryFile(root, deps.homeDir));\n return { root, store, records: store.list() };\n };\n\n const require = (records: readonly WorktreeRecord[], id: string): WorktreeRecord => {\n const record = records.find((entry) => entry.id === id);\n if (record === undefined) {\n throw new DoomGitExpectedError(\n 'worktree_not_found',\n `No worktree with id ${id}.`,\n false,\n 'Call list to see the current ids.',\n );\n }\n return record;\n };\n\n /**\n * Refuses to destroy or merge a worktree another live session is using.\n *\n * `parentSessionId` records who asked for the worktree. Two sessions in one\n * checkout share this registry, so without the check either could close the\n * other's work in progress by id.\n *\n * Once that session is gone there is no owner left to protect, and refusing\n * would strand the worktree: nothing else could ever close it.\n */\n const requireOwner = (context: WorktreeContext, record: WorktreeRecord, action: string): void => {\n if (record.parentSessionId === context.sessionId) return;\n if (!requireSessionService().isLive(record.parentSessionId)) return;\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n `Ask the session that created it to ${action} it, or close that session first.`,\n );\n };\n\n const peerFor = (\n context: WorktreeContext,\n record: WorktreeRecord,\n ): { sessionId: string; party: WorktreeMessageParty } => {\n if (context.sessionId === record.parentSessionId) return { sessionId: record.sessionId, party: 'parent' };\n if (context.sessionId === record.sessionId) return { sessionId: record.parentSessionId, party: 'child' };\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n 'Use a worktree owned by this session.',\n );\n };\n\n const requirePeerLive = (peerSessionId: string): void => {\n if (!requireSessionService().isLive(peerSessionId)) {\n throw new DoomGitExpectedError(\n 'worktree_peer_unavailable',\n 'The other worktree session is no longer live.',\n false,\n 'Start a new worktree session before sending a message.',\n );\n }\n };\n\n return {\n async spawn(context, request, options) {\n const { root, store, records } = await resolve(context);\n const refusal = refuseSpawn({ branch: request.branch, existing: records });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_exists', refusal, false, 'Pick another branch name, or close it.');\n }\n const baseRef = request.baseRef ?? (await git.currentBranch(root)) ?? 'HEAD';\n const id = shortId(randomUUID());\n const path = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: id,\n });\n\n // The worktree and the branch are made by one command, so they are undone\n // by one too. `worktree remove` leaves the branch behind, and a branch\n // nobody asked for is what a failed spawn used to leave on the floor.\n const cancelled = (): boolean => options?.signal?.aborted === true;\n const rollback = async (): Promise<string> => {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: request.branch }).catch(() => false);\n return deleted ? '' : ` The branch ${request.branch} has work on it and was kept.`;\n };\n\n options?.onProgress?.(`creating branch ${request.branch}\\u2026`);\n await git.addWorktree({ repositoryRoot: root, path, branch: request.branch, baseRef });\n\n // Checked here rather than only inside the session call: this is the\n // point where a checkout exists that nothing has recorded yet, which is\n // exactly the state an interrupted spawn used to leave behind.\n if (cancelled()) {\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled before the session started.${kept}`,\n false,\n 'The worktree was removed; run it again when you are ready.',\n );\n }\n\n // Before the session, because the session composes the extensions this\n // repository's config names and those live in build output git does not\n // track. A worktree without it starts and dies on the first package it\n // cannot resolve. Nothing here can fail the spawn: a repository with no\n // build output to mirror simply has none.\n options?.onProgress?.('mirroring build output\\u2026');\n mirror(root, path);\n\n // The worktree exists from here on, so every later failure has to leave\n // it removed or recorded. An unrecorded directory on disk is the one\n // outcome with no way back: nothing would ever list it again.\n options?.onProgress?.('starting session\\u2026');\n let sessionId: string;\n try {\n const session = await requireSessionService().create({\n cwd: path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n sessionProvenance: 'worktree',\n ...(options?.signal === undefined ? {} : { signal: options.signal }),\n });\n sessionId = session.sessionId;\n } catch (error) {\n const kept = await rollback();\n if (cancelled()) {\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled while the session was starting.${kept}`,\n false,\n 'The worktree was removed; run it again when you are ready.',\n );\n }\n if (error instanceof HubUnavailableError) {\n throw new DoomGitExpectedError(\n 'hub_unavailable',\n `${error.message}${kept}`,\n true,\n 'Start the cockpit and try again.',\n );\n }\n throw error;\n }\n\n const record: WorktreeRecord = {\n version: WORKTREE_RECORD_VERSION,\n id,\n branch: request.branch,\n baseRef,\n path,\n repositoryRoot: root,\n sessionId,\n parentSessionId: context.sessionId,\n status: 'running',\n createdAt: now().toISOString(),\n };\n try {\n store.replace([...records, record]);\n } catch (error) {\n // A live session with no record is the worst outcome available here:\n // it runs, and no id anything can name points at it. The session goes\n // back too, rather than being left for someone to find by hand.\n await requireSessionService()\n .close(sessionId)\n .catch(() => undefined);\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'registry_write_failed',\n `The worktree was created but the registry could not be written (${(error as Error).message}).${kept}`,\n true,\n 'Check the registry file for permissions or free space, then try again.',\n );\n }\n return record;\n },\n\n async close(context, id, force) {\n const { root, store, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'close');\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n const refusal = refuseClose({ record, dirtyFiles, force });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_dirty', refusal, false, 'Commit the work, or pass force: true.');\n }\n // The session is stopped before the directory goes. Removing a checkout\n // while a process still holds files open in it is how a half-removed\n // worktree and a stale git administrative entry are made.\n await requireSessionService().close(record.sessionId);\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force });\n store.replace(records.filter((entry) => entry.id !== id));\n return record;\n },\n\n async list(context) {\n const { store, records } = await resolve(context);\n const outcome = reconcile(records, probe);\n if (outcome.changed.length > 0) store.replace(outcome.records);\n return outcome.records;\n },\n\n async status(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n return { record, dirtyFiles };\n },\n\n async merge(context, id, message) {\n const { root, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'merge');\n // The merge lands in the parent, so the parent is what must be clean. A\n // dirty parent would mix the person's in-flight edits into a merge commit\n // they did not intend to make.\n const parentDirty = await git.dirtyFiles(root);\n if (parentDirty.length > 0) {\n throw new DoomGitExpectedError(\n 'worktree_dirty',\n `The parent checkout has ${String(parentDirty.length)} uncommitted file(s): ${parentDirty.slice(0, 10).join(', ')}.`,\n false,\n 'Commit or stash the parent checkout first.',\n );\n }\n await git.mergeBranch({\n repositoryRoot: root,\n branch: record.branch,\n ...(message === undefined ? {} : { message }),\n });\n return record;\n },\n\n /** Sends only between the parent session and its own worktree session. */\n async send(context, id, message) {\n if (Buffer.byteLength(message, 'utf8') > MAX_WORKTREE_MESSAGE_BYTES) {\n throw new DoomGitExpectedError(\n 'message_too_large',\n `A message may not exceed ${String(MAX_WORKTREE_MESSAGE_BYTES)} bytes.`,\n false,\n 'Send a shorter message.',\n );\n }\n const { records } = await resolve(context);\n const record = require(records, id);\n const peer = peerFor(context, record);\n requirePeerLive(peer.sessionId);\n requireMessageInbox().send(peer.sessionId, {\n version: 1,\n worktreeId: record.id,\n fromSessionId: context.sessionId,\n from: peer.party,\n text: message,\n sentAt: new Date().toISOString(),\n });\n },\n\n async messages(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n peerFor(context, record);\n return requireMessageInbox().receive(record.id);\n },\n\n async prune(context, dryRun) {\n const { root, store, records } = await resolve(context);\n const reconciled = reconcile(records, probe);\n const gitPaths = await git.listWorktreePaths(root);\n const dirtyByPath = new Map<string, readonly string[]>();\n for (const record of reconciled.records) {\n if (record.status === 'orphaned' && fs.existsSync(record.path)) {\n dirtyByPath.set(record.path, await git.dirtyFiles(record.path));\n }\n }\n const plan = planPrune({\n records: reconciled.records,\n gitPaths,\n worktreesRoot: worktreesRoot(deps.homeDir),\n dirtyByPath,\n });\n if (dryRun) {\n store.replace(reconciled.records);\n return plan;\n }\n for (const record of plan.remove) {\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force: false });\n }\n // The checkout goes, so the branch it was made for goes with it. Safe\n // delete only: git keeps anything holding commits, and the plan says\n // which ones it kept rather than leaving the reader to notice later.\n for (const record of [...plan.remove, ...plan.forget]) {\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: record.branch }).catch(() => false);\n if (!deleted) plan.keptBranches.push(record.branch);\n }\n const dropped = new Set([...plan.remove, ...plan.forget].map((record) => record.id));\n store.replace(reconciled.records.filter((record) => !dropped.has(record.id)));\n return plan;\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/services/worktreeOperations/index.ts"],"mappings":";;;;;iBAeiB;;EAEf;;EAEA;;iBAGe;EACf;EACA;EACA;;iBAGe;;EAEf,SAAS;;EAET,cAAc;;iBAGC;EACf,MAAM,SAAS,iBAAiB,SAAS,sBAAsB,UAAU,eAAe,QAAQ;EAChG,MAAM,SAAS,iBAAiB,YAAY,iBAAiB,QAAQ;EACrE,KAAK,SAAS,kBAAkB,QAAQ;EACxC,OAAO,SAAS,iBAAiB,aAAa;IAAU,QAAQ;IAAgB;;EAChF,MAAM,SAAS,iBAAiB,YAAY,mBAAmB,QAAQ;EACvE,MAAM,SAAS,iBAAiB,kBAAkB,QAAQ,kBAAkB;EAC5E,KAAK,SAAS,iBAAiB,YAAY,kBAAkB;EAC7D,SAAS,SAAS,iBAAiB,aAAa,QAA9B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/services/worktreeOperations/index.ts"],"mappings":";;;;;iBAeiB;;EAEf;;EAEA;;iBAGe;EACf;EACA;EACA;;iBAGe;;EAEf,SAAS;;EAET,cAAc;;iBAGC;EACf,MAAM,SAAS,iBAAiB,SAAS,sBAAsB,UAAU,eAAe,QAAQ;EAChG,MAAM,SAAS,iBAAiB,YAAY,iBAAiB,QAAQ;EACrE,KAAK,SAAS,kBAAkB,QAAQ;EACxC,OAAO,SAAS,iBAAiB,aAAa;IAAU,QAAQ;IAAgB;;EAChF,MAAM,SAAS,iBAAiB,YAAY,mBAAmB,QAAQ;EACvE,MAAM,SAAS,iBAAiB,kBAAkB,QAAQ,kBAAkB;EAC5E,KAAK,SAAS,iBAAiB,YAAY,kBAAkB;EAC7D,SAAS,SAAS,iBAAiB,aAAa,QAA9B"}
@@ -1,2 +0,0 @@
1
- import{mirrorComposition as e}from"../compositionMirror/index.mjs";import{DoomGitExpectedError as t,HubUnavailableError as n}from"../errors/index.mjs";import{repositoryId as r,repositoryLabel as i,shortId as a}from"../repositoryIdentity/index.mjs";import{registryFile as o,worktreesRoot as s}from"../paths/index.mjs";import{MAX_WORKTREE_MESSAGE_BYTES as c}from"../worktreeEvents/index.mjs";import{planPrune as l,reconcile as u,refuseClose as d,refuseSpawn as f,worktreeDirectory as p}from"../worktreeNaming/index.mjs";import{createWorktreeRegistry as m}from"../worktreeRegistry/index.mjs";import h from"node:fs";import{randomUUID as g}from"node:crypto";function _(_){let{git:v}=_,y=_.sessionService,b=_.mirror??e,x=_.now??(()=>new Date),S=()=>{if(y===void 0)throw new n(`The cockpit session service is unavailable. Start the cockpit and try again.`);return y},C=()=>{if(_.messageInbox===void 0)throw new n(`The Git message service is unavailable. Start the cockpit and try again.`);return _.messageInbox},w={alive:e=>S().isLive(e.sessionId),exists:e=>h.existsSync(e)},T=async e=>{let n=await v.repositoryRoot(e.cwd);if(n===void 0)throw new t(`not_a_repository`,`${e.cwd} is not inside a git repository.`,!1,`Run this from a repository checkout.`);let r=m(o(n,_.homeDir));return{root:n,store:r,records:r.list()}},E=(e,n)=>{let r=e.find(e=>e.id===n);if(r===void 0)throw new t(`worktree_not_found`,`No worktree with id ${n}.`,!1,`Call list to see the current ids.`);return r},D=(e,n,r)=>{if(n.parentSessionId!==e.sessionId&&S().isLive(n.parentSessionId))throw new t(`worktree_not_owned`,`Worktree ${n.id} belongs to another session.`,!1,`Ask the session that created it to ${r} it, or close that session first.`)},O=(e,n)=>{if(e.sessionId===n.parentSessionId)return{sessionId:n.sessionId,party:`parent`};if(e.sessionId===n.sessionId)return{sessionId:n.parentSessionId,party:`child`};throw new t(`worktree_not_owned`,`Worktree ${n.id} belongs to another session.`,!1,`Use a worktree owned by this session.`)},k=e=>{if(!S().isLive(e))throw new t(`worktree_peer_unavailable`,`The other worktree session is no longer live.`,!1,`Start a new worktree session before sending a message.`)};return{async spawn(e,o,c){let{root:l,store:u,records:d}=await T(e),m=f({branch:o.branch,existing:d});if(m!==void 0)throw new t(`worktree_exists`,m,!1,`Pick another branch name, or close it.`);let h=o.baseRef??await v.currentBranch(l)??`HEAD`,y=a(g()),C=p({worktreesRoot:s(_.homeDir),repositoryLabel:i(l),repositoryId:r(l),branch:o.branch,shortId:y}),w=()=>c?.signal?.aborted===!0,E=async()=>(await v.removeWorktree({repositoryRoot:l,path:C,force:!0}).catch(()=>void 0),await v.deleteBranch({repositoryRoot:l,branch:o.branch}).catch(()=>!1)?``:` The branch ${o.branch} has work on it and was kept.`);if(c?.onProgress?.(`creating branch ${o.branch}\u2026`),await v.addWorktree({repositoryRoot:l,path:C,branch:o.branch,baseRef:h}),w()){let e=await E();throw new t(`spawn_cancelled`,`Cancelled before the session started.${e}`,!1,`The worktree was removed; run it again when you are ready.`)}c?.onProgress?.(`mirroring build output…`),b(l,C),c?.onProgress?.(`starting session…`);let D;try{D=(await S().create({cwd:C,name:o.name??o.branch,parentSessionId:e.sessionId,sessionProvenance:`worktree`,...c?.signal===void 0?{}:{signal:c.signal}})).sessionId}catch(e){let r=await E();throw w()?new t(`spawn_cancelled`,`Cancelled while the session was starting.${r}`,!1,`The worktree was removed; run it again when you are ready.`):e instanceof n?new t(`hub_unavailable`,`${e.message}${r}`,!0,`Start the cockpit and try again.`):e}let O={version:1,id:y,branch:o.branch,baseRef:h,path:C,repositoryRoot:l,sessionId:D,parentSessionId:e.sessionId,status:`running`,createdAt:x().toISOString()};try{u.replace([...d,O])}catch(e){await S().close(D).catch(()=>void 0);let n=await E();throw new t(`registry_write_failed`,`The worktree was created but the registry could not be written (${e.message}).${n}`,!0,`Check the registry file for permissions or free space, then try again.`)}return O},async close(e,n,r){let{root:i,store:a,records:o}=await T(e),s=E(o,n);D(e,s,`close`);let c=h.existsSync(s.path)?await v.dirtyFiles(s.path):[],l=d({record:s,dirtyFiles:c,force:r});if(l!==void 0)throw new t(`worktree_dirty`,l,!1,`Commit the work, or pass force: true.`);return await S().close(s.sessionId),await v.removeWorktree({repositoryRoot:i,path:s.path,force:r}),a.replace(o.filter(e=>e.id!==n)),s},async list(e){let{store:t,records:n}=await T(e),r=u(n,w);return r.changed.length>0&&t.replace(r.records),r.records},async status(e,t){let{records:n}=await T(e),r=E(n,t);return{record:r,dirtyFiles:h.existsSync(r.path)?await v.dirtyFiles(r.path):[]}},async merge(e,n,r){let{root:i,records:a}=await T(e),o=E(a,n);D(e,o,`merge`);let s=await v.dirtyFiles(i);if(s.length>0)throw new t(`worktree_dirty`,`The parent checkout has ${String(s.length)} uncommitted file(s): ${s.slice(0,10).join(`, `)}.`,!1,`Commit or stash the parent checkout first.`);return await v.mergeBranch({repositoryRoot:i,branch:o.branch,...r===void 0?{}:{message:r}}),o},async send(e,n,r){if(Buffer.byteLength(r,`utf8`)>65536)throw new t(`message_too_large`,`A message may not exceed ${String(c)} bytes.`,!1,`Send a shorter message.`);let{records:i}=await T(e),a=E(i,n),o=O(e,a);k(o.sessionId),C().send(o.sessionId,{version:1,worktreeId:a.id,fromSessionId:e.sessionId,from:o.party,text:r,sentAt:new Date().toISOString()})},async messages(e,t){let{records:n}=await T(e),r=E(n,t);return O(e,r),C().receive(r.id)},async prune(e,t){let{root:n,store:r,records:i}=await T(e),a=u(i,w),o=await v.listWorktreePaths(n),c=new Map;for(let e of a.records)e.status===`orphaned`&&h.existsSync(e.path)&&c.set(e.path,await v.dirtyFiles(e.path));let d=l({records:a.records,gitPaths:o,worktreesRoot:s(_.homeDir),dirtyByPath:c});if(t)return r.replace(a.records),d;for(let e of d.remove)await v.removeWorktree({repositoryRoot:n,path:e.path,force:!1});for(let e of[...d.remove,...d.forget])await v.deleteBranch({repositoryRoot:n,branch:e.branch}).catch(()=>!1)||d.keptBranches.push(e.branch);let f=new Set([...d.remove,...d.forget].map(e=>e.id));return r.replace(a.records.filter(e=>!f.has(e.id))),d}}}export{_ as createWorktreeOperations};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/worktreeOperations/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\n\nimport type { DoomHubSessionService } from '@agimon-ai/doompi-core/hub-channel';\n\nimport { WORKTREE_RECORD_VERSION } from '../../types/worktreeRegistry';\nimport type { WorktreeGit, WorktreeRecord } from '../../types/worktreeRegistry';\nimport { mirrorComposition } from '../compositionMirror';\nimport { DoomGitExpectedError, HubUnavailableError } from '../errors';\nimport { registryFile, worktreesRoot } from '../paths';\nimport { repositoryId, repositoryLabel, shortId } from '../repositoryIdentity';\nimport { MAX_WORKTREE_MESSAGE_BYTES, type WorktreeMessageInbox, type WorktreeMessageParty } from '../worktreeEvents';\nimport { planPrune, reconcile, refuseClose, refuseSpawn, worktreeDirectory } from '../worktreeNaming';\nimport { createWorktreeRegistry } from '../worktreeRegistry';\n\nexport interface WorktreeContext {\n /** Where the calling session is working, used to find the repository. */\n cwd: string;\n /** The calling session, recorded as the new session's parent. */\n sessionId: string;\n}\n\nexport interface SpawnWorktreeRequest {\n branch: string;\n baseRef?: string;\n name?: string;\n}\n\nexport interface SpawnOptions {\n /** Aborted when the caller gives up, so a spawn nobody is waiting for stops. */\n signal?: AbortSignal;\n /** Phase labels, for a caller that can show them while the work runs. */\n onProgress?: (label: string) => void;\n}\n\nexport interface WorktreeOperations {\n spawn(context: WorktreeContext, request: SpawnWorktreeRequest, options?: SpawnOptions): Promise<WorktreeRecord>;\n close(context: WorktreeContext, id: string, force: boolean): Promise<WorktreeRecord>;\n list(context: WorktreeContext): Promise<WorktreeRecord[]>;\n status(context: WorktreeContext, id: string): Promise<{ record: WorktreeRecord; dirtyFiles: string[] }>;\n merge(context: WorktreeContext, id: string, message?: string): Promise<WorktreeRecord>;\n prune(context: WorktreeContext, dryRun: boolean): Promise<ReturnType<typeof planPrune>>;\n send(context: WorktreeContext, id: string, message: string): Promise<void>;\n messages(context: WorktreeContext, id: string): Promise<import('../worktreeEvents').WorktreeMessage[]>;\n}\n\nexport interface WorktreeOperationsDeps {\n git: WorktreeGit;\n /** The canonical hub-owned session lifecycle. */\n sessionService?: DoomHubSessionService;\n /** Session-local inbox for parent/worktree messages. */\n messageInbox?: WorktreeMessageInbox;\n /** Injected so a test never copies a real dependency tree. */\n mirror?: typeof mirrorComposition;\n homeDir?: string;\n /** Injected so a record's createdAt is reproducible in a test. */\n now?: () => Date;\n}\n\nexport function createWorktreeOperations(deps: WorktreeOperationsDeps): WorktreeOperations {\n const { git } = deps;\n const sessionService = deps.sessionService;\n const mirror = deps.mirror ?? mirrorComposition;\n const now = deps.now ?? (() => new Date());\n const requireSessionService = (): DoomHubSessionService => {\n if (sessionService === undefined)\n throw new HubUnavailableError('The cockpit session service is unavailable. Start the cockpit and try again.');\n return sessionService;\n };\n const requireMessageInbox = (): WorktreeMessageInbox => {\n if (deps.messageInbox === undefined)\n throw new HubUnavailableError('The Git message service is unavailable. Start the cockpit and try again.');\n return deps.messageInbox;\n };\n const probe = {\n alive: (record: WorktreeRecord) => requireSessionService().isLive(record.sessionId),\n exists: (path: string) => fs.existsSync(path),\n };\n\n /**\n * Everything an action needs about the repository it is acting on.\n *\n * Resolved per call rather than cached: a session's cwd is stable, but the\n * worktree registry is shared and another process may have written it since\n * the last call.\n */\n const resolve = async (context: WorktreeContext) => {\n const root = await git.repositoryRoot(context.cwd);\n if (root === undefined) {\n throw new DoomGitExpectedError(\n 'not_a_repository',\n `${context.cwd} is not inside a git repository.`,\n false,\n 'Run this from a repository checkout.',\n );\n }\n const store = createWorktreeRegistry(registryFile(root, deps.homeDir));\n return { root, store, records: store.list() };\n };\n\n const require = (records: readonly WorktreeRecord[], id: string): WorktreeRecord => {\n const record = records.find((entry) => entry.id === id);\n if (record === undefined) {\n throw new DoomGitExpectedError(\n 'worktree_not_found',\n `No worktree with id ${id}.`,\n false,\n 'Call list to see the current ids.',\n );\n }\n return record;\n };\n\n /**\n * Refuses to destroy or merge a worktree another live session is using.\n *\n * `parentSessionId` records who asked for the worktree. Two sessions in one\n * checkout share this registry, so without the check either could close the\n * other's work in progress by id.\n *\n * Once that session is gone there is no owner left to protect, and refusing\n * would strand the worktree: nothing else could ever close it.\n */\n const requireOwner = (context: WorktreeContext, record: WorktreeRecord, action: string): void => {\n if (record.parentSessionId === context.sessionId) return;\n if (!requireSessionService().isLive(record.parentSessionId)) return;\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n `Ask the session that created it to ${action} it, or close that session first.`,\n );\n };\n\n const peerFor = (\n context: WorktreeContext,\n record: WorktreeRecord,\n ): { sessionId: string; party: WorktreeMessageParty } => {\n if (context.sessionId === record.parentSessionId) return { sessionId: record.sessionId, party: 'parent' };\n if (context.sessionId === record.sessionId) return { sessionId: record.parentSessionId, party: 'child' };\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n 'Use a worktree owned by this session.',\n );\n };\n\n const requirePeerLive = (peerSessionId: string): void => {\n if (!requireSessionService().isLive(peerSessionId)) {\n throw new DoomGitExpectedError(\n 'worktree_peer_unavailable',\n 'The other worktree session is no longer live.',\n false,\n 'Start a new worktree session before sending a message.',\n );\n }\n };\n\n return {\n async spawn(context, request, options) {\n const { root, store, records } = await resolve(context);\n const refusal = refuseSpawn({ branch: request.branch, existing: records });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_exists', refusal, false, 'Pick another branch name, or close it.');\n }\n const baseRef = request.baseRef ?? (await git.currentBranch(root)) ?? 'HEAD';\n const id = shortId(randomUUID());\n const path = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: id,\n });\n\n // The worktree and the branch are made by one command, so they are undone\n // by one too. `worktree remove` leaves the branch behind, and a branch\n // nobody asked for is what a failed spawn used to leave on the floor.\n const cancelled = (): boolean => options?.signal?.aborted === true;\n const rollback = async (): Promise<string> => {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: request.branch }).catch(() => false);\n return deleted ? '' : ` The branch ${request.branch} has work on it and was kept.`;\n };\n\n options?.onProgress?.(`creating branch ${request.branch}\\u2026`);\n await git.addWorktree({ repositoryRoot: root, path, branch: request.branch, baseRef });\n\n // Checked here rather than only inside the session call: this is the\n // point where a checkout exists that nothing has recorded yet, which is\n // exactly the state an interrupted spawn used to leave behind.\n if (cancelled()) {\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled before the session started.${kept}`,\n false,\n 'The worktree was removed; run it again when you are ready.',\n );\n }\n\n // Before the session, because the session composes the extensions this\n // repository's config names and those live in build output git does not\n // track. A worktree without it starts and dies on the first package it\n // cannot resolve. Nothing here can fail the spawn: a repository with no\n // build output to mirror simply has none.\n options?.onProgress?.('mirroring build output\\u2026');\n mirror(root, path);\n\n // The worktree exists from here on, so every later failure has to leave\n // it removed or recorded. An unrecorded directory on disk is the one\n // outcome with no way back: nothing would ever list it again.\n options?.onProgress?.('starting session\\u2026');\n let sessionId: string;\n try {\n const session = await requireSessionService().create({\n cwd: path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n sessionProvenance: 'worktree',\n ...(options?.signal === undefined ? {} : { signal: options.signal }),\n });\n sessionId = session.sessionId;\n } catch (error) {\n const kept = await rollback();\n if (cancelled()) {\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled while the session was starting.${kept}`,\n false,\n 'The worktree was removed; run it again when you are ready.',\n );\n }\n if (error instanceof HubUnavailableError) {\n throw new DoomGitExpectedError(\n 'hub_unavailable',\n `${error.message}${kept}`,\n true,\n 'Start the cockpit and try again.',\n );\n }\n throw error;\n }\n\n const record: WorktreeRecord = {\n version: WORKTREE_RECORD_VERSION,\n id,\n branch: request.branch,\n baseRef,\n path,\n repositoryRoot: root,\n sessionId,\n parentSessionId: context.sessionId,\n status: 'running',\n createdAt: now().toISOString(),\n };\n try {\n store.replace([...records, record]);\n } catch (error) {\n // A live session with no record is the worst outcome available here:\n // it runs, and no id anything can name points at it. The session goes\n // back too, rather than being left for someone to find by hand.\n await requireSessionService()\n .close(sessionId)\n .catch(() => undefined);\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'registry_write_failed',\n `The worktree was created but the registry could not be written (${(error as Error).message}).${kept}`,\n true,\n 'Check the registry file for permissions or free space, then try again.',\n );\n }\n return record;\n },\n\n async close(context, id, force) {\n const { root, store, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'close');\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n const refusal = refuseClose({ record, dirtyFiles, force });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_dirty', refusal, false, 'Commit the work, or pass force: true.');\n }\n // The session is stopped before the directory goes. Removing a checkout\n // while a process still holds files open in it is how a half-removed\n // worktree and a stale git administrative entry are made.\n await requireSessionService().close(record.sessionId);\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force });\n store.replace(records.filter((entry) => entry.id !== id));\n return record;\n },\n\n async list(context) {\n const { store, records } = await resolve(context);\n const outcome = reconcile(records, probe);\n if (outcome.changed.length > 0) store.replace(outcome.records);\n return outcome.records;\n },\n\n async status(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n return { record, dirtyFiles };\n },\n\n async merge(context, id, message) {\n const { root, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'merge');\n // The merge lands in the parent, so the parent is what must be clean. A\n // dirty parent would mix the person's in-flight edits into a merge commit\n // they did not intend to make.\n const parentDirty = await git.dirtyFiles(root);\n if (parentDirty.length > 0) {\n throw new DoomGitExpectedError(\n 'worktree_dirty',\n `The parent checkout has ${String(parentDirty.length)} uncommitted file(s): ${parentDirty.slice(0, 10).join(', ')}.`,\n false,\n 'Commit or stash the parent checkout first.',\n );\n }\n await git.mergeBranch({\n repositoryRoot: root,\n branch: record.branch,\n ...(message === undefined ? {} : { message }),\n });\n return record;\n },\n\n /** Sends only between the parent session and its own worktree session. */\n async send(context, id, message) {\n if (Buffer.byteLength(message, 'utf8') > MAX_WORKTREE_MESSAGE_BYTES) {\n throw new DoomGitExpectedError(\n 'message_too_large',\n `A message may not exceed ${String(MAX_WORKTREE_MESSAGE_BYTES)} bytes.`,\n false,\n 'Send a shorter message.',\n );\n }\n const { records } = await resolve(context);\n const record = require(records, id);\n const peer = peerFor(context, record);\n requirePeerLive(peer.sessionId);\n requireMessageInbox().send(peer.sessionId, {\n version: 1,\n worktreeId: record.id,\n fromSessionId: context.sessionId,\n from: peer.party,\n text: message,\n sentAt: new Date().toISOString(),\n });\n },\n\n async messages(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n peerFor(context, record);\n return requireMessageInbox().receive(record.id);\n },\n\n async prune(context, dryRun) {\n const { root, store, records } = await resolve(context);\n const reconciled = reconcile(records, probe);\n const gitPaths = await git.listWorktreePaths(root);\n const dirtyByPath = new Map<string, readonly string[]>();\n for (const record of reconciled.records) {\n if (record.status === 'orphaned' && fs.existsSync(record.path)) {\n dirtyByPath.set(record.path, await git.dirtyFiles(record.path));\n }\n }\n const plan = planPrune({\n records: reconciled.records,\n gitPaths,\n worktreesRoot: worktreesRoot(deps.homeDir),\n dirtyByPath,\n });\n if (dryRun) {\n store.replace(reconciled.records);\n return plan;\n }\n for (const record of plan.remove) {\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force: false });\n }\n // The checkout goes, so the branch it was made for goes with it. Safe\n // delete only: git keeps anything holding commits, and the plan says\n // which ones it kept rather than leaving the reader to notice later.\n for (const record of [...plan.remove, ...plan.forget]) {\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: record.branch }).catch(() => false);\n if (!deleted) plan.keptBranches.push(record.branch);\n }\n const dropped = new Set([...plan.remove, ...plan.forget].map((record) => record.id));\n store.replace(reconciled.records.filter((record) => !dropped.has(record.id)));\n return plan;\n },\n };\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../atomicJson/index.cjs");function t(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.id==`string`&&t.id!==``&&typeof t.path==`string`&&typeof t.branch==`string`&&typeof t.sessionId==`string`}function n(n){return{list(){let r=e.readJson(n);return!r||r.version!==1||!Array.isArray(r.entries)?[]:r.entries.filter(t)},replace(t){e.writeJsonAtomic(n,{version:1,entries:t})}}}exports.createWorktreeRegistry=n;
2
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["readJson"],"sources":["../../../src/services/worktreeRegistry/index.ts"],"sourcesContent":["import {\n WORKTREE_RECORD_VERSION,\n type WorktreeRecord,\n type WorktreeRegistryFile,\n type WorktreeRegistryStore,\n} from '../../types/worktreeRegistry';\nimport { readJson, writeJsonAtomic } from '../atomicJson';\n\nfunction isRecord(value: unknown): value is WorktreeRecord {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Partial<WorktreeRecord>;\n return (\n record.version === WORKTREE_RECORD_VERSION &&\n typeof record.id === 'string' &&\n record.id !== '' &&\n typeof record.path === 'string' &&\n typeof record.branch === 'string' &&\n typeof record.sessionId === 'string'\n );\n}\n\n/**\n * The worktree registry for one repository, as a single JSON file.\n *\n * One file rather than a file per worktree, and read-modify-write rather than\n * merging: every write comes from a tool call in one session's turn, so there\n * is no concurrent writer to reconcile with. Choosing the simpler shape now\n * means a reader can see the whole registry in one place; if concurrent writers\n * ever appear, that is the point to revisit, not before.\n *\n * A file that does not parse is treated as empty rather than fatal. Losing\n * track of worktrees is recoverable through prune; refusing to run is not.\n */\nexport function createWorktreeRegistry(file: string): WorktreeRegistryStore {\n return {\n list() {\n const parsed = readJson<WorktreeRegistryFile>(file);\n if (!parsed || parsed.version !== WORKTREE_RECORD_VERSION || !Array.isArray(parsed.entries)) return [];\n return parsed.entries.filter(isRecord);\n },\n replace(entries) {\n writeJsonAtomic(file, { version: WORKTREE_RECORD_VERSION, entries } satisfies WorktreeRegistryFile);\n },\n };\n}\n"],"mappings":"2CAQA,SAAS,EAAS,EAAyC,CACzD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAS,EACf,OACE,EAAO,UAAA,GACP,OAAO,EAAO,IAAO,UACrB,EAAO,KAAO,IACd,OAAO,EAAO,MAAS,UACvB,OAAO,EAAO,QAAW,UACzB,OAAO,EAAO,WAAc,QAEhC,CAcA,SAAgB,EAAuB,EAAqC,CAC1E,MAAO,CACL,MAAO,CACL,IAAM,EAASA,EAAAA,SAA+B,CAAI,EAElD,MADI,CAAC,GAAU,EAAO,UAAA,GAAuC,CAAC,MAAM,QAAQ,EAAO,OAAO,EAAU,CAAC,EAC9F,EAAO,QAAQ,OAAO,CAAQ,CACvC,EACA,QAAQ,EAAS,CACf,EAAA,gBAAgB,EAAM,CAAE,QAAA,EAAkC,SAAQ,CAAgC,CACpG,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- import{readJson as e,writeJsonAtomic as t}from"../atomicJson/index.mjs";function n(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.id==`string`&&t.id!==``&&typeof t.path==`string`&&typeof t.branch==`string`&&typeof t.sessionId==`string`}function r(r){return{list(){let t=e(r);return!t||t.version!==1||!Array.isArray(t.entries)?[]:t.entries.filter(n)},replace(e){t(r,{version:1,entries:e})}}}export{r as createWorktreeRegistry};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/services/worktreeRegistry/index.ts"],"sourcesContent":["import {\n WORKTREE_RECORD_VERSION,\n type WorktreeRecord,\n type WorktreeRegistryFile,\n type WorktreeRegistryStore,\n} from '../../types/worktreeRegistry';\nimport { readJson, writeJsonAtomic } from '../atomicJson';\n\nfunction isRecord(value: unknown): value is WorktreeRecord {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Partial<WorktreeRecord>;\n return (\n record.version === WORKTREE_RECORD_VERSION &&\n typeof record.id === 'string' &&\n record.id !== '' &&\n typeof record.path === 'string' &&\n typeof record.branch === 'string' &&\n typeof record.sessionId === 'string'\n );\n}\n\n/**\n * The worktree registry for one repository, as a single JSON file.\n *\n * One file rather than a file per worktree, and read-modify-write rather than\n * merging: every write comes from a tool call in one session's turn, so there\n * is no concurrent writer to reconcile with. Choosing the simpler shape now\n * means a reader can see the whole registry in one place; if concurrent writers\n * ever appear, that is the point to revisit, not before.\n *\n * A file that does not parse is treated as empty rather than fatal. Losing\n * track of worktrees is recoverable through prune; refusing to run is not.\n */\nexport function createWorktreeRegistry(file: string): WorktreeRegistryStore {\n return {\n list() {\n const parsed = readJson<WorktreeRegistryFile>(file);\n if (!parsed || parsed.version !== WORKTREE_RECORD_VERSION || !Array.isArray(parsed.entries)) return [];\n return parsed.entries.filter(isRecord);\n },\n replace(entries) {\n writeJsonAtomic(file, { version: WORKTREE_RECORD_VERSION, entries } satisfies WorktreeRegistryFile);\n },\n };\n}\n"],"mappings":"wEAQA,SAAS,EAAS,EAAyC,CACzD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAS,EACf,OACE,EAAO,UAAA,GACP,OAAO,EAAO,IAAO,UACrB,EAAO,KAAO,IACd,OAAO,EAAO,MAAS,UACvB,OAAO,EAAO,QAAW,UACzB,OAAO,EAAO,WAAc,QAEhC,CAcA,SAAgB,EAAuB,EAAqC,CAC1E,MAAO,CACL,MAAO,CACL,IAAM,EAAS,EAA+B,CAAI,EAElD,MADI,CAAC,GAAU,EAAO,UAAA,GAAuC,CAAC,MAAM,QAAQ,EAAO,OAAO,EAAU,CAAC,EAC9F,EAAO,QAAQ,OAAO,CAAQ,CACvC,EACA,QAAQ,EAAS,CACf,EAAgB,EAAM,CAAE,QAAA,EAAkC,SAAQ,CAAgC,CACpG,CACF,CACF"}
@@ -1,2 +0,0 @@
1
- const e=require("../schemas/runWorktreeTool.cjs"),t=require("../services/runWorktree/index.cjs");function n(n){return{name:t.RUN_WORKTREE_TOOL_NAME,label:`Worktree`,description:t.RUN_WORKTREE_DESCRIPTION,parameters:e.RunWorktreeToolSchema,prepareArguments:t.validateParams,execute:async(e,r,i,a,o)=>{let s=t.validateParams(r);return await t.executeRunWorktreeTool(s,n,{cwd:o.cwd,sessionId:o.sessionManager.getSessionId()},{...i===void 0?{}:{signal:i},onProgress:e=>a?.({content:[{type:`text`,text:e}],details:{action:s.action}})})}}}exports.RUN_WORKTREE_TOOL_NAME=t.RUN_WORKTREE_TOOL_NAME,exports.createRunWorktreeTool=n,exports.validateParams=t.validateParams;
2
- //# sourceMappingURL=runWorktree.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runWorktree.cjs","names":["RUN_WORKTREE_TOOL_NAME","RUN_WORKTREE_DESCRIPTION","RunWorktreeToolSchema","validateParams","executeRunWorktreeTool"],"sources":["../../src/tools/runWorktree.ts"],"sourcesContent":["import type { AgentToolResult } from '@earendil-works/pi-agent-core';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\n\nimport { RunWorktreeToolSchema, type RunWorktreeToolParams } from '../schemas/runWorktreeTool';\nimport {\n executeRunWorktreeTool,\n RUN_WORKTREE_DESCRIPTION,\n RUN_WORKTREE_TOOL_NAME,\n validateParams,\n type RunWorktreeToolDetails,\n} from '../services/runWorktree';\nimport type { WorktreeOperations } from '../services/worktreeOperations';\n\nexport { RUN_WORKTREE_TOOL_NAME, validateParams };\nexport type { RunWorktreeToolDetails };\n\n/** Creates the typed Pi worktree tool declaration. */\nexport function createRunWorktreeTool(\n operations: WorktreeOperations,\n): ToolDefinition<typeof RunWorktreeToolSchema, RunWorktreeToolDetails> {\n const tool: ToolDefinition<typeof RunWorktreeToolSchema, RunWorktreeToolDetails> = {\n name: RUN_WORKTREE_TOOL_NAME,\n label: 'Worktree',\n description: RUN_WORKTREE_DESCRIPTION,\n parameters: RunWorktreeToolSchema,\n prepareArguments: validateParams,\n // The signal and the progress callback are the host's two answers to a\n // call that takes minutes. Dropping them is what let an interrupted spawn\n // run on with nobody waiting for it, and left the caller staring at\n // nothing while it did.\n execute: async (_id, rawParams, signal, onUpdate, ctx) => {\n const params = validateParams(rawParams) as RunWorktreeToolParams;\n return (await executeRunWorktreeTool(\n params,\n operations,\n {\n cwd: ctx.cwd,\n sessionId: ctx.sessionManager.getSessionId(),\n },\n {\n ...(signal === undefined ? {} : { signal }),\n onProgress: (label) =>\n onUpdate?.({ content: [{ type: 'text', text: label }], details: { action: params.action } }),\n },\n )) as AgentToolResult<RunWorktreeToolDetails>;\n },\n };\n return tool;\n}\n"],"mappings":"iGAiBA,SAAgB,EACd,EACsE,CA4BtE,MAAO,CA1BL,KAAMA,EAAAA,uBACN,MAAO,WACP,YAAaC,EAAAA,yBACb,WAAYC,EAAAA,sBACZ,iBAAkBC,EAAAA,eAKlB,QAAS,MAAO,EAAK,EAAW,EAAQ,EAAU,IAAQ,CACxD,IAAM,EAASA,EAAAA,eAAe,CAAS,EACvC,OAAQ,MAAMC,EAAAA,uBACZ,EACA,EACA,CACE,IAAK,EAAI,IACT,UAAW,EAAI,eAAe,aAAa,CAC7C,EACA,CACE,GAAI,IAAW,IAAA,GAAY,CAAC,EAAI,CAAE,QAAO,EACzC,WAAa,GACX,IAAW,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAM,CAAC,EAAG,QAAS,CAAE,OAAQ,EAAO,MAAO,CAAE,CAAC,CAC/F,CACF,CACF,CAEQ,CACZ"}
@@ -1,2 +0,0 @@
1
- import{RunWorktreeToolSchema as e}from"../schemas/runWorktreeTool.mjs";import{RUN_WORKTREE_DESCRIPTION as t,RUN_WORKTREE_TOOL_NAME as n,executeRunWorktreeTool as r,validateParams as i}from"../services/runWorktree/index.mjs";function a(a){return{name:n,label:`Worktree`,description:t,parameters:e,prepareArguments:i,execute:async(e,t,n,o,s)=>{let c=i(t);return await r(c,a,{cwd:s.cwd,sessionId:s.sessionManager.getSessionId()},{...n===void 0?{}:{signal:n},onProgress:e=>o?.({content:[{type:`text`,text:e}],details:{action:c.action}})})}}}export{n as RUN_WORKTREE_TOOL_NAME,a as createRunWorktreeTool,i as validateParams};
2
- //# sourceMappingURL=runWorktree.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runWorktree.mjs","names":[],"sources":["../../src/tools/runWorktree.ts"],"sourcesContent":["import type { AgentToolResult } from '@earendil-works/pi-agent-core';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\n\nimport { RunWorktreeToolSchema, type RunWorktreeToolParams } from '../schemas/runWorktreeTool';\nimport {\n executeRunWorktreeTool,\n RUN_WORKTREE_DESCRIPTION,\n RUN_WORKTREE_TOOL_NAME,\n validateParams,\n type RunWorktreeToolDetails,\n} from '../services/runWorktree';\nimport type { WorktreeOperations } from '../services/worktreeOperations';\n\nexport { RUN_WORKTREE_TOOL_NAME, validateParams };\nexport type { RunWorktreeToolDetails };\n\n/** Creates the typed Pi worktree tool declaration. */\nexport function createRunWorktreeTool(\n operations: WorktreeOperations,\n): ToolDefinition<typeof RunWorktreeToolSchema, RunWorktreeToolDetails> {\n const tool: ToolDefinition<typeof RunWorktreeToolSchema, RunWorktreeToolDetails> = {\n name: RUN_WORKTREE_TOOL_NAME,\n label: 'Worktree',\n description: RUN_WORKTREE_DESCRIPTION,\n parameters: RunWorktreeToolSchema,\n prepareArguments: validateParams,\n // The signal and the progress callback are the host's two answers to a\n // call that takes minutes. Dropping them is what let an interrupted spawn\n // run on with nobody waiting for it, and left the caller staring at\n // nothing while it did.\n execute: async (_id, rawParams, signal, onUpdate, ctx) => {\n const params = validateParams(rawParams) as RunWorktreeToolParams;\n return (await executeRunWorktreeTool(\n params,\n operations,\n {\n cwd: ctx.cwd,\n sessionId: ctx.sessionManager.getSessionId(),\n },\n {\n ...(signal === undefined ? {} : { signal }),\n onProgress: (label) =>\n onUpdate?.({ content: [{ type: 'text', text: label }], details: { action: params.action } }),\n },\n )) as AgentToolResult<RunWorktreeToolDetails>;\n },\n };\n return tool;\n}\n"],"mappings":"gOAiBA,SAAgB,EACd,EACsE,CA4BtE,MAAO,CA1BL,KAAM,EACN,MAAO,WACP,YAAa,EACb,WAAY,EACZ,iBAAkB,EAKlB,QAAS,MAAO,EAAK,EAAW,EAAQ,EAAU,IAAQ,CACxD,IAAM,EAAS,EAAe,CAAS,EACvC,OAAQ,MAAM,EACZ,EACA,EACA,CACE,IAAK,EAAI,IACT,UAAW,EAAI,eAAe,aAAa,CAC7C,EACA,CACE,GAAI,IAAW,IAAA,GAAY,CAAC,EAAI,CAAE,QAAO,EACzC,WAAa,GACX,IAAW,CAAE,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAM,CAAM,CAAC,EAAG,QAAS,CAAE,OAAQ,EAAO,MAAO,CAAE,CAAC,CAC/F,CACF,CACF,CAEQ,CACZ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.cts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;YAGY;iBAEK;EACf;EACA,OAAO;;iBAGQ;EACf,WAAW,QAAQ;;iBAGJ;EACf,SAAS;EACT,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"extension.d.mts","names":[],"sources":["../../src/types/extension.ts"],"mappings":";;YAGY;iBAEK;EACf;EACA,OAAO;;iBAGQ;EACf,WAAW,QAAQ;;iBAGJ;EACf,SAAS;EACT,YAAY"}
@@ -1,2 +0,0 @@
1
- exports.GIT_WORKTREES_TYPE=`git_worktrees`;
2
- //# sourceMappingURL=webWorktrees.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webWorktrees.cjs","names":[],"sources":["../../src/types/webWorktrees.ts"],"sourcesContent":["/**\n * The cockpit's view of this session's worktrees.\n *\n * DESIGN PATTERNS:\n * - Declarations only. The channel frame type is a constant so the hub and the\n * page cannot drift apart on a string.\n * - A view is the subset of a record the dock needs, not the record. The\n * registry may hold paths and provenance the page has no business rendering.\n *\n * AVOID:\n * - Adding behaviour here. This file is imported by browser code, so it must\n * stay free of node builtins.\n */\n\n/** The data channel carrying one session's worktrees to the page. */\nexport const GIT_WORKTREES_TYPE = 'git_worktrees';\n\n/** One worktree, as the activity dock and its panel render it. */\nexport interface WorktreeView {\n id: string;\n branch: string;\n path: string;\n /** The session the hub started for this worktree, when it is still live. */\n sessionId: string | null;\n /** True once the directory the record names has gone missing. */\n orphaned: boolean;\n /**\n * True when the session that asked for this worktree is gone.\n *\n * An owned worktree is only ever shown to its own session. This flag marks\n * the exception: once the parent is dead nobody would see the worktree at\n * all, so every session in the repository does, and may close it.\n */\n unowned: boolean;\n}\n\n/**\n * What the panel asks the hub channel to do.\n *\n * The dock acts through its own channel rather than through the agent: a\n * worktree made from the panel and one made by the tool take the same\n * `worktreeOperations` path, but the panel no longer has to spend a turn of\n * the conversation to get there.\n */\nexport type GitWorktreesCommand =\n | { action: 'create'; branch: string; baseRef?: string }\n | { action: 'close'; id: string; force?: boolean };\n\nexport interface GitWorktreesPayload {\n worktrees: WorktreeView[];\n /** Label of the operation in flight, absent when the session is idle. */\n pending?: string;\n /** The last failure, cleared when the next command starts. */\n error?: string;\n}\n"],"mappings":""}
@@ -1,2 +0,0 @@
1
- const e=`git_worktrees`;export{e as GIT_WORKTREES_TYPE};
2
- //# sourceMappingURL=webWorktrees.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webWorktrees.mjs","names":[],"sources":["../../src/types/webWorktrees.ts"],"sourcesContent":["/**\n * The cockpit's view of this session's worktrees.\n *\n * DESIGN PATTERNS:\n * - Declarations only. The channel frame type is a constant so the hub and the\n * page cannot drift apart on a string.\n * - A view is the subset of a record the dock needs, not the record. The\n * registry may hold paths and provenance the page has no business rendering.\n *\n * AVOID:\n * - Adding behaviour here. This file is imported by browser code, so it must\n * stay free of node builtins.\n */\n\n/** The data channel carrying one session's worktrees to the page. */\nexport const GIT_WORKTREES_TYPE = 'git_worktrees';\n\n/** One worktree, as the activity dock and its panel render it. */\nexport interface WorktreeView {\n id: string;\n branch: string;\n path: string;\n /** The session the hub started for this worktree, when it is still live. */\n sessionId: string | null;\n /** True once the directory the record names has gone missing. */\n orphaned: boolean;\n /**\n * True when the session that asked for this worktree is gone.\n *\n * An owned worktree is only ever shown to its own session. This flag marks\n * the exception: once the parent is dead nobody would see the worktree at\n * all, so every session in the repository does, and may close it.\n */\n unowned: boolean;\n}\n\n/**\n * What the panel asks the hub channel to do.\n *\n * The dock acts through its own channel rather than through the agent: a\n * worktree made from the panel and one made by the tool take the same\n * `worktreeOperations` path, but the panel no longer has to spend a turn of\n * the conversation to get there.\n */\nexport type GitWorktreesCommand =\n | { action: 'create'; branch: string; baseRef?: string }\n | { action: 'close'; id: string; force?: boolean };\n\nexport interface GitWorktreesPayload {\n worktrees: WorktreeView[];\n /** Label of the operation in flight, absent when the session is idle. */\n pending?: string;\n /** The last failure, cleared when the next command starts. */\n error?: string;\n}\n"],"mappings":"AAeA,MAAa,EAAqB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeRegistry.d.cts","names":[],"sources":["../../src/types/worktreeRegistry.ts"],"mappings":";;;;;;;;;;;;;;;;YAgBY;qBAEC;iBAEI;EACf,gBAAgB;;EAEhB;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA,QAAQ;;EAER"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeRegistry.d.mts","names":[],"sources":["../../src/types/worktreeRegistry.ts"],"mappings":";;;;;;;;;;;;;;;;YAgBY;qBAEC;iBAEI;EACf,gBAAgB;;EAEhB;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA,QAAQ;;EAER"}
@@ -1,33 +0,0 @@
1
- import { defineWebPlugin } from '@agimon-ai/doompi-core/web';
2
-
3
- import { gitToolRenderers } from '../web/components/toolRenderers';
4
- import { WorktreesActivitySection } from '../web/components/WorktreesActivitySection';
5
- import { worktreesTab } from '../web/components/WorktreesPanel';
6
- import { startWorktreeRuntime, worktreeActivitySource, worktreesChannel } from '../web/stores/worktreesActivityStore';
7
-
8
- /** The named export the generated plugin registry imports. */
9
- export const webPlugin = defineWebPlugin({
10
- id: 'git',
11
- session: {
12
- channels: [worktreesChannel],
13
- // The panel commands the hub over its own channel, which needs the page's
14
- // hub socket rather than the per-session frame sender the slots carry.
15
- start: startWorktreeRuntime,
16
- activityGroups: [
17
- {
18
- name: 'git',
19
- keys: 'g w',
20
- // The source keeps the group present while idle, which is exactly when
21
- // its launcher matters: a repository with no worktrees is where someone
22
- // wants to make the first one.
23
- activeSource: worktreeActivitySource,
24
- // A standing worktree is not background work in progress; it is state.
25
- marksBackgroundWork: false,
26
- transientTab: worktreesTab,
27
- order: 30,
28
- },
29
- ],
30
- activitySections: [{ id: 'git', component: WorktreesActivitySection }],
31
- toolRenderers: gitToolRenderers,
32
- },
33
- });
@@ -1,55 +0,0 @@
1
- /**
2
- * The cockpit's view of this session's worktrees.
3
- *
4
- * DESIGN PATTERNS:
5
- * - Declarations only. The channel frame type is a constant so the hub and the
6
- * page cannot drift apart on a string.
7
- * - A view is the subset of a record the dock needs, not the record. The
8
- * registry may hold paths and provenance the page has no business rendering.
9
- *
10
- * AVOID:
11
- * - Adding behaviour here. This file is imported by browser code, so it must
12
- * stay free of node builtins.
13
- */
14
-
15
- /** The data channel carrying one session's worktrees to the page. */
16
- export const GIT_WORKTREES_TYPE = 'git_worktrees';
17
-
18
- /** One worktree, as the activity dock and its panel render it. */
19
- export interface WorktreeView {
20
- id: string;
21
- branch: string;
22
- path: string;
23
- /** The session the hub started for this worktree, when it is still live. */
24
- sessionId: string | null;
25
- /** True once the directory the record names has gone missing. */
26
- orphaned: boolean;
27
- /**
28
- * True when the session that asked for this worktree is gone.
29
- *
30
- * An owned worktree is only ever shown to its own session. This flag marks
31
- * the exception: once the parent is dead nobody would see the worktree at
32
- * all, so every session in the repository does, and may close it.
33
- */
34
- unowned: boolean;
35
- }
36
-
37
- /**
38
- * What the panel asks the hub channel to do.
39
- *
40
- * The dock acts through its own channel rather than through the agent: a
41
- * worktree made from the panel and one made by the tool take the same
42
- * `worktreeOperations` path, but the panel no longer has to spend a turn of
43
- * the conversation to get there.
44
- */
45
- export type GitWorktreesCommand =
46
- | { action: 'create'; branch: string; baseRef?: string }
47
- | { action: 'close'; id: string; force?: boolean };
48
-
49
- export interface GitWorktreesPayload {
50
- worktrees: WorktreeView[];
51
- /** Label of the operation in flight, absent when the session is idle. */
52
- pending?: string;
53
- /** The last failure, cleared when the next command starts. */
54
- error?: string;
55
- }
@@ -1,78 +0,0 @@
1
- /**
2
- * The worktree registry: what this package knows about the worktrees it made.
3
- *
4
- * It deliberately does not mirror `git worktree list`. Git knows which
5
- * worktrees exist; only this registry knows which of them this package created,
6
- * which session runs in each, and which process to signal. Reconciling the two
7
- * is how orphans are found, so the two views are kept separate on purpose.
8
- */
9
-
10
- /**
11
- * A worktree's lifecycle as this package sees it.
12
- *
13
- * `orphaned` is a finding, not a cleanup: the session process is gone or the
14
- * directory has vanished, but the worktree may still hold uncommitted work, so
15
- * the record is kept until someone decides what to do with it.
16
- */
17
- export type WorktreeStatus = 'spawning' | 'running' | 'closing' | 'orphaned';
18
-
19
- export const WORKTREE_RECORD_VERSION = 1;
20
-
21
- export interface WorktreeRecord {
22
- version: typeof WORKTREE_RECORD_VERSION;
23
- /** This registry's own id, distinct from the session id, and stable across restarts. */
24
- id: string;
25
- /** The branch checked out in the worktree. */
26
- branch: string;
27
- /** The ref the branch was created from, kept so a merge can name its base. */
28
- baseRef: string;
29
- /** Absolute path to the worktree directory. */
30
- path: string;
31
- /** Absolute path to the repository the worktree belongs to. */
32
- repositoryRoot: string;
33
- /** The session spawned into the worktree. */
34
- sessionId: string;
35
- /** The session that asked for the worktree; the rail nests one under the other. */
36
- parentSessionId: string;
37
- status: WorktreeStatus;
38
- /** ISO 8601. */
39
- createdAt: string;
40
- }
41
-
42
- export interface WorktreeRegistryFile {
43
- version: typeof WORKTREE_RECORD_VERSION;
44
- entries: readonly WorktreeRecord[];
45
- }
46
-
47
- /** Reads the registry back, treating anything unusable as an empty registry. */
48
- export interface WorktreeRegistryStore {
49
- list(): WorktreeRecord[];
50
- /** Replaces the whole file. One writer per repository, so no merge is attempted. */
51
- replace(entries: readonly WorktreeRecord[]): void;
52
- }
53
-
54
- /** The git operations this package needs, named in its own vocabulary. */
55
- export interface WorktreeGit {
56
- addWorktree(input: { repositoryRoot: string; path: string; branch: string; baseRef: string }): Promise<void>;
57
- removeWorktree(input: { repositoryRoot: string; path: string; force: boolean }): Promise<void>;
58
- /**
59
- * Safe branch delete, reporting refusal rather than throwing.
60
- *
61
- * `worktree remove` leaves the branch behind, so undoing a spawn needs this
62
- * too. It is always the safe form: git refuses a branch holding unmerged
63
- * work, and false says the branch is still there rather than pretending it
64
- * went.
65
- */
66
- deleteBranch(input: { repositoryRoot: string; branch: string }): Promise<boolean>;
67
- pruneWorktrees(repositoryRoot: string): Promise<void>;
68
- /** Paths git currently lists for the repository, used to find orphans. */
69
- listWorktreePaths(repositoryRoot: string): Promise<string[]>;
70
- /** Files with uncommitted changes, empty when the tree is clean. */
71
- dirtyFiles(path: string): Promise<string[]>;
72
- /** The repository root for a directory, or undefined when it is not a repository. */
73
- repositoryRoot(cwd: string): Promise<string | undefined>;
74
- /** The branch currently checked out, used as the default base ref. */
75
- currentBranch(cwd: string): Promise<string | undefined>;
76
- /** Merges a worktree's branch into whatever the given root has checked out. */
77
- mergeBranch(input: { repositoryRoot: string; branch: string; message?: string }): Promise<void>;
78
- }
@@ -1,45 +0,0 @@
1
- import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';
2
- import { MessageItem, MessageItemBody, MessageItemHeader, toolTone } from '@agimon-ai/doompi-web-components';
3
-
4
- const ACTION_LABEL: Readonly<Record<string, string>> = {
5
- spawn_worktree: 'Create worktree',
6
- close_worktree: 'Close worktree',
7
- list: 'List worktrees',
8
- status: 'Worktree status',
9
- merge: 'Merge worktree',
10
- prune: 'Prune worktrees',
11
- send: 'Message worktree',
12
- messages: 'Read worktree messages',
13
- };
14
-
15
- /**
16
- * The timeline item for `run_worktree`.
17
- *
18
- * DESIGN PATTERNS:
19
- * - Built on MessageItem, like every other tool renderer in the cockpit. That
20
- * is what gives it the shared collapse toggle and the RUNNING/OK/ERROR badge
21
- * rather than a hand-rolled ellipsis that never resolves.
22
- * - The action and its subject go in the heading because that is what a reader
23
- * scanning a transcript needs: "close_worktree a1b2c3d4" answers the question
24
- * the raw JSON arguments make them decode.
25
- * - A create takes as long as the cockpit needs to start a session, and the
26
- * tool streams each phase into `output`, so the body is where the wait is
27
- * visible.
28
- */
29
- export function RunWorktreeToolMessage({ args, output, running, isError }: ToolMessageRenderProps) {
30
- const action = typeof args.action === 'string' ? args.action : '';
31
- const subject = typeof args.branch === 'string' ? args.branch : typeof args.id === 'string' ? args.id : '';
32
-
33
- return (
34
- <MessageItem tone={toolTone({ running, isError })} expandable={output !== ''}>
35
- <MessageItemHeader title={ACTION_LABEL[action] ?? 'Worktree'}>
36
- {subject === '' ? null : <span className="min-w-0 truncate text-doom-dim">{subject}</span>}
37
- </MessageItemHeader>
38
- {output === '' ? null : (
39
- <MessageItemBody>
40
- <pre className={`whitespace-pre-wrap text-xs ${isError ? 'text-doom-red' : 'text-doom-dim'}`}>{output}</pre>
41
- </MessageItemBody>
42
- )}
43
- </MessageItem>
44
- );
45
- }
@@ -1,80 +0,0 @@
1
- import type { WebPluginSlotProps } from '@agimon-ai/doompi-core/web';
2
- /**
3
- * The '# git' group's body in the activity dock.
4
- *
5
- * DESIGN PATTERNS:
6
- * - One line per worktree, the same rule the runners rail follows: enough to
7
- * notice it, not enough to work with it. The group's own name opens the tab
8
- * that has room.
9
- * - Idle still shows the launcher. A repository with no worktrees is the state
10
- * where someone most wants to make one.
11
- *
12
- * AVOID:
13
- * - Doing work here. This section reports and launches; the panel and the tool
14
- * own everything else.
15
- */
16
- import { Button, Dot, Spinner, StatusBadge } from '@agimon-ai/doompi-web-components';
17
- import { useStore } from '@tanstack/react-store';
18
-
19
- import type { WorktreeView } from '../../types/webWorktrees';
20
- import { worktreeActivity } from '../stores/worktreesActivityStore';
21
- import { worktreesTab } from './WorktreesPanel';
22
-
23
- export function WorktreesActivitySection({ sessionId, openTransientTab }: WebPluginSlotProps) {
24
- const session = useStore(worktreeActivity.store, (state) => worktreeActivity.select(state, sessionId));
25
-
26
- if (session.worktrees.length === 0 && session.pending === undefined) {
27
- return (
28
- <div className="flex items-center gap-2 px-1">
29
- <p data-testid="activity-summary-git" className="px-1 text-xs text-doom-faint">
30
- idle
31
- </p>
32
- {sessionId === null ? null : (
33
- <Button
34
- variant="link"
35
- size="xs"
36
- data-testid="activity-git-create"
37
- className="px-0"
38
- onClick={() => openTransientTab(worktreesTab())}
39
- >
40
- create a worktree
41
- </Button>
42
- )}
43
- </div>
44
- );
45
- }
46
-
47
- return (
48
- <div data-testid="activity-git-worktrees" className="flex flex-col gap-0.5">
49
- {session.pending === undefined ? null : (
50
- <span className="flex min-w-0 items-center gap-1.5 px-1">
51
- <Spinner className="h-3 w-3 shrink-0 text-doom-faint" label={session.pending} />
52
- <span className="min-w-0 truncate text-xs text-doom-dim">{session.pending}</span>
53
- </span>
54
- )}
55
- {session.worktrees.map((worktree: WorktreeView) => (
56
- <Button
57
- key={worktree.id}
58
- variant="ghost"
59
- size="card"
60
- data-testid={`activity-git-worktree-${worktree.id}`}
61
- className="min-w-0 gap-1.5 px-1"
62
- onClick={() => openTransientTab(worktreesTab())}
63
- >
64
- <Dot tone={worktree.orphaned ? 'red' : worktree.sessionId === null ? 'muted' : 'blue'} />
65
- <span className="min-w-0 flex-1 truncate text-left text-xs font-bold text-doom-hi">{worktree.branch}</span>
66
- {worktree.orphaned ? (
67
- <StatusBadge tone="error" size="xs">
68
- orphaned
69
- </StatusBadge>
70
- ) : null}
71
- {worktree.unowned ? (
72
- <StatusBadge tone="info" size="xs">
73
- unowned
74
- </StatusBadge>
75
- ) : null}
76
- </Button>
77
- ))}
78
- </div>
79
- );
80
- }